diff --git a/.mypy.ini b/.mypy.ini index 062d415..a5ba247 100644 --- a/.mypy.ini +++ b/.mypy.ini @@ -5,4 +5,5 @@ follow_imports = silent ignore_missing_imports = True show_column_numbers = True pretty = True -strict = True \ No newline at end of file +strict = True +mypy_path=$MYPY_CONFIG_FILE_DIR/typings \ No newline at end of file diff --git a/Go-features-readme.md b/Go-features-readme.md new file mode 100644 index 0000000..37df411 --- /dev/null +++ b/Go-features-readme.md @@ -0,0 +1,69 @@ +# LLEF Experimental Go Features Readme + +LLEF now has experimental support for stripped Go binaries. This document contains examples of LLEF's Go features, including screenshots of what LLEF's analysis output displays compared with previous versions of LLEF. These features are tested across a range of Go versions since 1.7, which was released in 2016. + +## Resolving and displaying Go types + +Go binaries store metadata on complex data types which are used in the program, even when the binary has been stripped. From this metadata, field names and types can be recovered for `struct`, `map`, `array` and `slice` types. As an example, consider the following `struct` definition in a Go source file: + +```go +type Tool struct { + Name string `json:"name"` + Developer string `json:"developer"` + Score int `json:"score"` +} +func demo() { + tools := []Tool{ + {Name: "LLEF", Developer: "Foundry Zero", Score: 10}, + {Name: "Binder Trace", Developer: "Foundry Zero", Score: 10}, + } + jsonData, _ := json.Marshal(tools) + fmt.Println(string(jsonData)) +} +``` + +In the above program listing, `tools` is an `array` type of `Tool` objects, which is passed into `json.Marshal`. The definition of `json.Marshal` includes an `any` type for the input argument. To keep track of the input data type, the Go compiler loads a pointer to read-only metadata for the `[]Tool` data type at runtime, followed by a pointer to the raw object data in memory. + +In previous versions of LLEF, when a breakpoint is set on the call to `json.Marshal`, the register view shows the following: + +![Previous LLEF analysis output.](./assets/go-examples/json-mashal-old.png) + +With Go features enabled, LLEF can now identify the pointer `0x4c3300` points to a data type definition for `[]Tool`, and used that definition to display the contents of the data pointer in `ebx`: + +![New LLEF analysis output.](./assets/go-examples/json-marshal-new.png) + +A new command `go get-type` has been added to find Go data type definitions present within the binary. When an argument is given to the command, LLEF will to match the name of the type to the definition present in the Go binary. If no match can be found, LLEF will display similar entries: + +![Go get-type command.](./assets/go-examples/go-get-type.png) + +When no arguments are given, all data types are displayed. + +If a data type pointer is not passed into a called function alongside the raw data type pointer, LLEF will not be able to determine the matching data type. In this case, an analyst can manually use the command `go unpack-type` to attempt to display a data pointer using a given data type definition. + +![Manually unpacking types.](./assets/go-examples/go-unpack-type-command.png) + +## Resolving Go Function Names + +Go binaries store function symbols which are used during exception handling, even for stripped binaries. LLEF can now recover these function symbols and display them: + +![Display of function symbols in listing view.](./assets/go-examples/function-name-recovery.png) + +A new command `go find-func` has been added to search through function symbols, which is especially useful for finding where to set breakpoints. When no argument is given, all function symbols are displayed. + +![Finding functions by name.](./assets/go-examples/go-find-func-command.png) + +## Go call stack unwinding + +LLEF can now display the Go call stack on x86 and x86_64 architectures. This is displayed in the listing view, but can also be displayed with the command `go backtrace`. + +![Go call stack display.](./assets/go-examples/go-backtrace-command.png) + +## Before and After + +### Before + +![GIF Before](./assets/go-examples/go-mode-disabled.gif) + +### After + +![GIF After](./assets/go-examples/go-mode-enabled.gif) \ No newline at end of file diff --git a/README.md b/README.md index 2b43dd3..c702f36 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,12 @@ Settings are stored in a file `.llef` located in your home directory formatted a | rebase_addresses | Boolean | Enable/disable address rebase output | | rebase_offset | Int | Set the rebase offset (default 0x100000) | | show_all_registers | Boolean | Enable/disable extended register output | -| enable_darwin_heap_scan | Boolean | Enable/disable more accurate heap scanning for Darwin-based platforms. Uses the Darwin malloc introspection API, executing code in the address space of the target application using LLDB's evaluation engine. | +| enable_darwin_heap_scan | Boolean | Enable/disable more accurate heap scanning for Darwin-based platforms. Uses the Darwin malloc introspection API, executing code in the address space of the target application using LLDB's evaluation engine | +| max_trace_length | Int | Set the maximum length of the call stack backtrace to display | +| stack_view_size | Int | Set the number of entries in the stack read to display | +| max_disassembly_length | Int | Set the maximum number of instructions to disassemble and display around the current PC | +| go_support_level | String | Control Golang-specific analysis. `disable` / `auto` (default) / `force`. For performance reasons, Go support in Windows binaries requires `force`. | +| go_confidence_threshold | String | Set the confidence threshold (`low` / `medium` / `high`) for Go objects to be shown in the context view | #### llefcolorsettings Allows setting LLEF GUI colors: @@ -111,6 +116,7 @@ Supported colors: BLUE, GREEN, YELLOW, RED, PINK, CYAN, GREY | dereferenced_register_color | | frame_argument_name_color | | read_memory_address_color | +| go_type_color | #### Hexdump View memory contents with: @@ -179,6 +185,36 @@ aabacadaea [+] Found in $8 at index 0 (little endian) ``` +#### (Go) Unpack Type + +``` +(lldb) go unpack-type 0xc000130000 []main.Country +[{Name:'Japan' Capital:'Tokyo' Continent:'Asia'} {Name:'Germany' Capital:'Berlin' Continent:'Europe'}] +(lldb) go unpack-type 0xc000130000 []main.Country --depth 1 +[0xc000142000.. 0xc000142030..] +(lldb) go unpack-type 0xc000142000 main.Country +{Name:'Japan' Capital:'Tokyo' Continent:'Asia'} +(lldb) go unpack-type 0xc000142000 [6]uintptr +[0xc000114140 0x5 0xc000114145 0x5 0xc00011414c 0x4] +``` + +#### (Go) Find Function +``` +(lldb) go find-func main.main +0x55c6894c0280 - main.main (file address = 0x4c0280) +(lldb) go find-func 0x55c689454a3a +0x55c689454a20 - runtime.(*moduledata).textAddr (file address = 0x454a20) +``` + +#### (Go) Get Type +``` +(lldb) go get-type json.mapEncoder --depth 3 +json.mapEncoder = struct { elemEnc func(*json.encodeState, struct { typ_ *abi.Type; ptr unsafe.Pointer; flag uintptr }, struct { quoted bool; escapeHTML bool }) } +Size in bytes: 0x8 +(lldb) go get-type json.encodeState --depth 1 +json.encodeState = struct { Buffer bytes.Buffer; ptrLevel uint; ptrSeen map[interface {}]struct {} } +Size in bytes: 0x38 +``` ### Breakpoint hook This is automatic and prints all the currently implemented information at a break point. diff --git a/arch/__init__.py b/arch/__init__.py index 6ae3829..7e29fed 100644 --- a/arch/__init__.py +++ b/arch/__init__.py @@ -1,7 +1,5 @@ """Arch module __init__.py""" -from typing import Type - from lldb import SBTarget from arch.aarch64 import Aarch64 @@ -12,7 +10,12 @@ from arch.x86_64 import X86_64 from common.constants import MSG_TYPE from common.output_util import print_message -from common.util import extract_arch_from_triple + + +def extract_arch_from_triple(triple: str) -> str: + """Extracts the architecture from triple string.""" + return triple.split("-")[0] + # macOS devices running arm chips identify as arm64. # aarch64 and arm64 backends have been merged, so alias arm64 to aarch64. @@ -29,13 +32,13 @@ } -def get_arch(target: SBTarget) -> Type[BaseArch]: +def get_arch(target: SBTarget) -> type[BaseArch]: """Get the architecture of a given target""" arch = extract_arch_from_triple(target.triple) return get_arch_from_str(arch) -def get_arch_from_str(arch: str) -> Type[BaseArch]: +def get_arch_from_str(arch: str) -> type[BaseArch]: """Get the architecture class from string""" if arch in supported_arch: return supported_arch[arch] diff --git a/arch/aarch64.py b/arch/aarch64.py index 2e5d44d..2fe30e2 100644 --- a/arch/aarch64.py +++ b/arch/aarch64.py @@ -10,6 +10,8 @@ class Aarch64(BaseArch): bits = 64 + max_instr_size = 4 + gpr_registers = [ "x0", "x1", diff --git a/arch/arm.py b/arch/arm.py index cbc868f..cdb5ca7 100644 --- a/arch/arm.py +++ b/arch/arm.py @@ -10,6 +10,8 @@ class Arm(BaseArch): bits = 32 + max_instr_size = 4 + gpr_registers = [ "r0", "r1", diff --git a/arch/base_arch.py b/arch/base_arch.py index 8a6bd19..1b7dda9 100644 --- a/arch/base_arch.py +++ b/arch/base_arch.py @@ -2,7 +2,6 @@ from abc import ABC, abstractmethod from dataclasses import dataclass -from typing import Dict, List @dataclass @@ -10,7 +9,7 @@ class FlagRegister: """FlagRegister dataclass to store register name / bitmask associations""" name: str - bit_masks: Dict[str, int] + bit_masks: dict[str, int] class BaseArch(ABC): @@ -23,7 +22,12 @@ def bits(self) -> int: @property @abstractmethod - def gpr_registers(self) -> List[str]: + def max_instr_size(self) -> int: + """Max instruction size (bytes) property""" + + @property + @abstractmethod + def gpr_registers(self) -> list[str]: """GPR register property""" @property @@ -33,5 +37,5 @@ def gpr_key(self) -> str: @property @abstractmethod - def flag_registers(self) -> List[FlagRegister]: + def flag_registers(self) -> list[FlagRegister]: """List of flag registers with associated bit masks""" diff --git a/arch/i386.py b/arch/i386.py index 4ad1dee..b90ddb2 100644 --- a/arch/i386.py +++ b/arch/i386.py @@ -10,6 +10,8 @@ class I386(BaseArch): bits = 32 + max_instr_size = 15 + gpr_registers = [ "eax", "ebx", diff --git a/arch/ppc.py b/arch/ppc.py index c0fd218..c3ba930 100644 --- a/arch/ppc.py +++ b/arch/ppc.py @@ -10,6 +10,8 @@ class PPC(BaseArch): bits = 32 + max_instr_size = 4 + gpr_registers = [ "r0", "r1", diff --git a/arch/x86_64.py b/arch/x86_64.py index 97680da..d639c29 100644 --- a/arch/x86_64.py +++ b/arch/x86_64.py @@ -10,16 +10,15 @@ class X86_64(BaseArch): bits = 64 + max_instr_size = 15 + gpr_registers = [ "rax", "rbx", "rcx", "rdx", - "rsp", - "rbp", - "rsi", "rdi", - "rip", + "rsi", "r8", "r9", "r10", @@ -28,6 +27,9 @@ class X86_64(BaseArch): "r13", "r14", "r15", + "rsp", + "rbp", + "rip", ] gpr_key = "general purpose" diff --git a/assets/go-examples/function-name-recovery.png b/assets/go-examples/function-name-recovery.png new file mode 100644 index 0000000..adbdc2c Binary files /dev/null and b/assets/go-examples/function-name-recovery.png differ diff --git a/assets/go-examples/go-backtrace-command.png b/assets/go-examples/go-backtrace-command.png new file mode 100644 index 0000000..000f832 Binary files /dev/null and b/assets/go-examples/go-backtrace-command.png differ diff --git a/assets/go-examples/go-find-func-command.png b/assets/go-examples/go-find-func-command.png new file mode 100644 index 0000000..1c57b86 Binary files /dev/null and b/assets/go-examples/go-find-func-command.png differ diff --git a/assets/go-examples/go-get-type.png b/assets/go-examples/go-get-type.png new file mode 100644 index 0000000..a483fca Binary files /dev/null and b/assets/go-examples/go-get-type.png differ diff --git a/assets/go-examples/go-mode-disabled.gif b/assets/go-examples/go-mode-disabled.gif new file mode 100644 index 0000000..d056492 Binary files /dev/null and b/assets/go-examples/go-mode-disabled.gif differ diff --git a/assets/go-examples/go-mode-enabled.gif b/assets/go-examples/go-mode-enabled.gif new file mode 100644 index 0000000..da06be4 Binary files /dev/null and b/assets/go-examples/go-mode-enabled.gif differ diff --git a/assets/go-examples/go-unpack-type-command.png b/assets/go-examples/go-unpack-type-command.png new file mode 100644 index 0000000..bee4d57 Binary files /dev/null and b/assets/go-examples/go-unpack-type-command.png differ diff --git a/assets/go-examples/json-marshal-new.png b/assets/go-examples/json-marshal-new.png new file mode 100644 index 0000000..df6ffe7 Binary files /dev/null and b/assets/go-examples/json-marshal-new.png differ diff --git a/assets/go-examples/json-mashal-old.png b/assets/go-examples/json-mashal-old.png new file mode 100644 index 0000000..3b1320d Binary files /dev/null and b/assets/go-examples/json-mashal-old.png differ diff --git a/commands/base_command.py b/commands/base_command.py index f9b6d72..db42eb3 100644 --- a/commands/base_command.py +++ b/commands/base_command.py @@ -1,7 +1,7 @@ """Base command definition.""" from abc import ABC, abstractmethod -from typing import Type +from typing import Any, Union from lldb import SBCommandReturnObject, SBDebugger, SBExecutionContext @@ -11,7 +11,7 @@ class BaseCommand(ABC): """An abstract base class for all commands.""" - alias_set = {} + alias_set: dict[Any, Any] = {} @abstractmethod def __init__(self) -> None: @@ -19,7 +19,7 @@ def __init__(self) -> None: @property @abstractmethod - def container(self) -> Type[BaseContainer]: + def container(self) -> Union[type[BaseContainer], None]: """Container property.""" @property diff --git a/commands/base_settings.py b/commands/base_settings.py index aa644fb..eba709d 100644 --- a/commands/base_settings.py +++ b/commands/base_settings.py @@ -3,11 +3,12 @@ import argparse import shlex from abc import ABC, abstractmethod -from typing import Any, Dict +from typing import Any, Union from lldb import SBCommandReturnObject, SBDebugger, SBExecutionContext from commands.base_command import BaseCommand +from common.base_settings import BaseLLEFSettings from common.output_util import output_line @@ -16,9 +17,9 @@ class BaseSettingsCommand(BaseCommand, ABC): program: str = "" container = None - settings = None + settings: Union[BaseLLEFSettings, None] = None - def __init__(self, debugger: SBDebugger, __: Dict[Any, Any]) -> None: + def __init__(self, debugger: SBDebugger, __: dict[Any, Any]) -> None: super().__init__() self.parser = self.get_command_parser() @@ -51,8 +52,11 @@ def __call__( output_line(self.__class__.get_long_help()) return + if self.settings is None: + raise AttributeError("Class not properly initialised: self.settings is None") + if args.action == "list": - self.settings.list() + self.settings.list_settings() elif args.action == "save": self.settings.save() elif args.action == "reload": diff --git a/commands/checksec.py b/commands/checksec.py index ccedf73..ace632e 100644 --- a/commands/checksec.py +++ b/commands/checksec.py @@ -1,7 +1,7 @@ """Checksec command class.""" import argparse -from typing import Any, Dict +from typing import Any, Union from lldb import SBCommandReturnObject, SBDebugger, SBError, SBExecutionContext, SBTarget @@ -39,9 +39,9 @@ class ChecksecCommand(BaseCommand): program: str = "checksec" container = None - context_handler = None + context_handler: Union[ContextHandler, None] = None - def __init__(self, debugger: SBDebugger, __: Dict[Any, Any]) -> None: + def __init__(self, debugger: SBDebugger, __: dict[Any, Any]) -> None: super().__init__() self.parser = self.get_command_parser() self.context_handler = ContextHandler(debugger) @@ -62,7 +62,7 @@ def get_long_help() -> str: """Return a longer help message""" return ChecksecCommand.get_command_parser().format_help() - def get_executable_type(self, target: SBTarget): + def get_executable_type(self, target: SBTarget) -> int: """ Get executable type for a given @target ELF file. @@ -71,7 +71,7 @@ def get_executable_type(self, target: SBTarget): """ return read_program_int(target, 0x10, 2) - def get_program_header_permission(self, target: SBTarget, target_header_type: int): + def get_program_header_permission(self, target: SBTarget, target_header_type: int) -> Union[int, None]: """ Get value of the permission field from a program header entry. @@ -79,7 +79,7 @@ def get_program_header_permission(self, target: SBTarget, target_header_type: in :param target_header_type: The type of the program header entry. :return: An integer between 0 and 7 representing the permission. Returns 'None' if program header is not found. """ - arch = get_arch(target).bits + arch = get_arch(target)().bits if arch == ARCH_BITS.BITS_32: program_header_offset = read_program_int(target, PROGRAM_HEADER_OFFSET_32BIT_OFFSET, 4) @@ -103,7 +103,7 @@ def get_program_header_permission(self, target: SBTarget, target_header_type: in return permission - def get_dynamic_entry(self, target: SBTarget, target_entry_type: int): + def get_dynamic_entry(self, target: SBTarget, target_entry_type: int) -> Union[int, None]: """ Get value for a given entry type in the .dynamic section table. @@ -126,7 +126,7 @@ def get_dynamic_entry(self, target: SBTarget, target_entry_type: int): return target_entry_value - def check_security(self, target: SBTarget) -> Dict[str, SECURITY_CHECK]: + def check_security(self, target: SBTarget) -> dict[SECURITY_FEATURE, SECURITY_CHECK]: """ Checks the following security features on the target executable: - Stack Canary @@ -162,7 +162,7 @@ def check_security(self, target: SBTarget) -> Dict[str, SECURITY_CHECK]: else: checks[SECURITY_FEATURE.NX_SUPPORT] = SECURITY_CHECK.NO except MemoryError as error: - print_message(MSG_TYPE.ERROR, error) + print_message(MSG_TYPE.ERROR, str(error)) checks[SECURITY_FEATURE.NX_SUPPORT] = SECURITY_CHECK.UNKNOWN # Check for PIE Support @@ -172,7 +172,7 @@ def check_security(self, target: SBTarget) -> Dict[str, SECURITY_CHECK]: else: checks[SECURITY_FEATURE.PIE_SUPPORT] = SECURITY_CHECK.NO except MemoryError as error: - print_message(MSG_TYPE.ERROR, error) + print_message(MSG_TYPE.ERROR, str(error)) checks[SECURITY_FEATURE.PIE_SUPPORT] = SECURITY_CHECK.UNKNOWN # Check for Partial RelRO @@ -182,7 +182,7 @@ def check_security(self, target: SBTarget) -> Dict[str, SECURITY_CHECK]: else: checks[SECURITY_FEATURE.PARTIAL_RELRO] = SECURITY_CHECK.NO except MemoryError as error: - print_message(MSG_TYPE.ERROR, error) + print_message(MSG_TYPE.ERROR, str(error)) checks[SECURITY_FEATURE.PARTIAL_RELRO] = SECURITY_CHECK.UNKNOWN # Check for Full RelRO @@ -221,6 +221,9 @@ def __call__( ) -> None: """Handles the invocation of the checksec command""" + if self.context_handler is None: + raise AttributeError("Class not properly initialised: self.context_handler is None") + self.context_handler.refresh(exe_ctx) target = exe_ctx.GetTarget() diff --git a/commands/color_settings.py b/commands/color_settings.py index 6c4d015..c05c3e1 100644 --- a/commands/color_settings.py +++ b/commands/color_settings.py @@ -1,7 +1,7 @@ """llefcolorsettings command class.""" import argparse -from typing import Any, Dict +from typing import Any from lldb import SBDebugger @@ -15,7 +15,7 @@ class ColorSettingsCommand(BaseSettingsCommand): program: str = "llefcolorsettings" container = None - def __init__(self, debugger: SBDebugger, dictionary: Dict[Any, Any]) -> None: + def __init__(self, debugger: SBDebugger, dictionary: dict[Any, Any]) -> None: super().__init__(debugger, dictionary) self.settings = LLEFColorSettings() diff --git a/commands/context.py b/commands/context.py index 9685145..756b8d1 100644 --- a/commands/context.py +++ b/commands/context.py @@ -2,7 +2,7 @@ import argparse import shlex -from typing import Any, Dict +from typing import Any from lldb import SBCommandReturnObject, SBDebugger, SBExecutionContext @@ -17,7 +17,7 @@ class ContextCommand(BaseCommand): program: str = "context" container = None - def __init__(self, debugger: SBDebugger, __: Dict[Any, Any]) -> None: + def __init__(self, debugger: SBDebugger, __: dict[Any, Any]) -> None: super().__init__() self.parser = self.get_command_parser() self.context_handler = ContextHandler(debugger) diff --git a/commands/dereference.py b/commands/dereference.py index 628d733..ec37cd4 100644 --- a/commands/dereference.py +++ b/commands/dereference.py @@ -2,7 +2,7 @@ import argparse import shlex -from typing import Any, Dict, List +from typing import Any, Union from lldb import ( SBAddress, @@ -30,9 +30,9 @@ class DereferenceCommand(BaseCommand): program: str = "dereference" container = None - context_handler = None + context_handler: Union[ContextHandler, None] = None - def __init__(self, debugger: SBDebugger, __: Dict[Any, Any]) -> None: + def __init__(self, debugger: SBDebugger, __: dict[Any, Any]) -> None: super().__init__() self.parser = self.get_command_parser() self.context_handler = ContextHandler(debugger) @@ -87,8 +87,12 @@ def read_instruction(self, target: SBTarget, address: int) -> SBInstruction: return instruction_list.GetInstructionAtIndex(0) def dereference_last_address( - self, data: list, target: SBTarget, process: SBProcess, regions: SBMemoryRegionInfoList - ): + self, + data: list[Union[int, str]], + target: SBTarget, + process: SBProcess, + regions: Union[SBMemoryRegionInfoList, None], + ) -> None: """ Memory data at the last address (second to last in @data list) is either disassembled to an instruction or converted to a string or neither. @@ -99,6 +103,8 @@ def dereference_last_address( :param regions: List of memory regions of the process. """ last_address = data[-2] + if isinstance(last_address, str): + return if is_code(last_address, process, target, regions): instruction = self.read_instruction(target, last_address) @@ -112,7 +118,9 @@ def dereference_last_address( if string != "": data[-1] = color_string(string, self.color_settings.string_color) - def dereference(self, address: int, target: SBTarget, process: SBProcess, regions: SBMemoryRegionInfoList) -> List: + def dereference( + self, address: int, target: SBTarget, process: SBProcess, regions: Union[SBMemoryRegionInfoList, None] + ) -> list[Union[int, str]]: """ Dereference a memory @address until it reaches data that cannot be resolved to an address. Memory data at the last address is either disassembled to an instruction or converted to a string or neither. @@ -125,7 +133,7 @@ def dereference(self, address: int, target: SBTarget, process: SBProcess, region :param regions: List of memory regions of the process. """ - data = [] + data: list[Union[int, str]] = [] error = SBError() while error.Success(): @@ -142,7 +150,7 @@ def dereference(self, address: int, target: SBTarget, process: SBProcess, region return data - def print_dereference_result(self, result: List, offset: int): + def print_dereference_result(self, result: list[Union[int, str]], offset: int) -> None: """Format and output the results of dereferencing an address.""" output = color_string(hex_or_str(result[0]), TERM_COLORS.CYAN.name, rwrap=GLYPHS.VERTICAL_LINE.value) if offset >= 0: @@ -171,6 +179,9 @@ def __call__( else: base = start_address + if self.context_handler is None: + raise AttributeError("Class not properly initialised: self.context_handler is None") + self.context_handler.refresh(exe_ctx) address_size = exe_ctx.target.GetAddressByteSize() @@ -178,5 +189,5 @@ def __call__( end_address = start_address + address_size * lines for address in range(start_address, end_address, address_size): offset = address - base - result = self.dereference(address, exe_ctx.target, exe_ctx.process, self.context_handler.regions) - self.print_dereference_result(result, offset) + deref_result = self.dereference(address, exe_ctx.target, exe_ctx.process, self.context_handler.regions) + self.print_dereference_result(deref_result, offset) diff --git a/commands/golang.py b/commands/golang.py new file mode 100644 index 0000000..2a57c8a --- /dev/null +++ b/commands/golang.py @@ -0,0 +1,526 @@ +"""Go-specific command classes.""" + +import argparse +import os +import shlex +from typing import Any, Union + +from lldb import SBCommandReturnObject, SBDebugger, SBExecutionContext + +from commands.base_command import BaseCommand +from commands.base_container import BaseContainer +from common.constants import MSG_TYPE +from common.context_handler import ContextHandler +from common.golang.analysis import go_get_backtrace +from common.golang.constants import GO_DEFAULT_UNPACK_DEPTH +from common.golang.data import GoDataBad +from common.golang.state import GoState +from common.golang.static import setup_go +from common.golang.type_getter import TypeGetter +from common.golang.types import ExtractInfo +from common.golang.util import go_calculate_base_pointer, go_context_analysis, go_find_func +from common.output_util import output_line, print_message +from common.settings import LLEFSettings +from common.state import LLEFState +from common.util import check_process, check_version, hex_int, positive_int + +GO_DISABLED_MSG = "Go support is disabled. Re-enable using `llefsettings set go_support_level auto`." + + +class GolangContainer(BaseContainer): + """Creates a container for the Go command. Sub commands are implemented in inner classes""" + + container_verb: str = "go" + + @staticmethod + def get_short_help() -> str: + return "go (find-func|get-type|unpack-type|backtrace|reanalyse)" + + @staticmethod + def get_long_help() -> str: + return ( + "The subcommands use information from a Go binary to display information about functions and types and " + "to unpack objects at runtime depending on their type." + ) + + +class GolangFindFuncCommand(BaseCommand): + """Implements the 'find-func' subcommand""" + + program: str = "find-func" + container: type[BaseContainer] = GolangContainer + parser: argparse.ArgumentParser + context_handler: ContextHandler + settings: LLEFSettings + + @classmethod + def get_command_parser(cls) -> argparse.ArgumentParser: + """Get the command parser.""" + parser = argparse.ArgumentParser() + parser.add_argument( + "target", + default=None, + nargs="?", + help="Either a code address, or a function name, or omitted", + ) + return parser + + @staticmethod + def get_short_help() -> str: + return "Usage: go find-func [address|name]" + + @staticmethod + def get_long_help() -> str: + return ( + "If given an address, prints the Go function containing that address." + + os.linesep + + "If given a name, prints the address of the Go function with that name." + + os.linesep + + "If not given an argument, prints a table of all known functions. File addresses appear in parentheses." + + os.linesep + + GolangFindFuncCommand.get_command_parser().format_help() + ) + + def __init__(self, debugger: SBDebugger, _: dict[Any, Any]) -> None: + self.parser = self.get_command_parser() + self.context_handler = ContextHandler(debugger) + self.settings = LLEFSettings(debugger) + + @check_version("15.0.0") + @check_process + def __call__( + self, + debugger: SBDebugger, + command: str, + exe_ctx: SBExecutionContext, + result: SBCommandReturnObject, + ) -> None: + """Handles the invocation of 'go find-func' command""" + args = self.parser.parse_args(shlex.split(command)) + address_or_name = args.target + + self.context_handler.refresh(exe_ctx) + + if self.settings.go_support_level == "disable": + print_message(MSG_TYPE.ERROR, GO_DISABLED_MSG) + elif not go_context_analysis(self.settings): + print_message(MSG_TYPE.ERROR, "The binary does not appear to be a Go binary.") + else: + + if address_or_name is None: + # Print a table + output_line("LOAD_ADDRESS (FILE_ADDRESS) - NAME") + for entry, f in LLEFState.go_state.pclntab_info.func_mapping: + output_line(f"{hex(entry)} ({hex(f.file_addr)}) - {f.name}") + else: + + try: + # User has typed in a numeric address + address = int(address_or_name, 0) + function_mapping = go_find_func(address) + if function_mapping is not None: + (entry, gofunc) = function_mapping + output_line(f"{hex(entry)} - {gofunc.name} (file address = {hex(gofunc.file_addr)})") + else: + print_message(MSG_TYPE.ERROR, f"Could not find function containing address {hex(address)}") + + except ValueError: + # User has typed in a string name + name = address_or_name + + success = False + for entry, gofunc in LLEFState.go_state.pclntab_info.func_mapping: + if name in gofunc.name: + output_line(f"{hex(entry)} - {gofunc.name} (file address = {hex(gofunc.file_addr)})") + success = True + # Don't break: there are potentially multiple matches. + + if not success: + print_message(MSG_TYPE.ERROR, f"Could not find function called '{name}'") + + +class GolangGetTypeCommand(BaseCommand): + """Implements the 'get-type' subcommand""" + + program: str = "get-type" + container: type[BaseContainer] = GolangContainer + parser: argparse.ArgumentParser + context_handler: ContextHandler + settings: LLEFSettings + + type_getter: Union[TypeGetter, None] + + @classmethod + def get_command_parser(cls) -> argparse.ArgumentParser: + """Get the command parser.""" + parser = argparse.ArgumentParser() + parser.add_argument( + "target", + default=None, + nargs="?", + help="Either a type information structure address, or a type name, or omitted", + ) + parser.add_argument( + "-d", + "--depth", + type=positive_int, + default=GO_DEFAULT_UNPACK_DEPTH, + help=f"Depth to unpack child types, default is {GO_DEFAULT_UNPACK_DEPTH}", + ) + + return parser + + @staticmethod + def get_short_help() -> str: + return "Usage: go get-type [address|name] [--depth n]" + + @staticmethod + def get_long_help() -> str: + return ( + "If given an address, prints a deconstruction of the Go type struct at that address." + + os.linesep + + "If given a name, prints a deconstruction of the Go type with that name." + + os.linesep + + "If not given an argument, prints a table of all known types." + + os.linesep + + "The depth argument specifies how deeply to follow and unpack child types. " + + f"It defaults to {GO_DEFAULT_UNPACK_DEPTH}" + + os.linesep + + GolangGetTypeCommand.get_command_parser().format_help() + ) + + def __init__(self, debugger: SBDebugger, _: dict[Any, Any]) -> None: + self.parser = self.get_command_parser() + self.context_handler = ContextHandler(debugger) + self.settings = LLEFSettings(debugger) + + self.type_getter = None # For now, and will be set when we have a context later. + + @check_version("15.0.0") + @check_process + def __call__( + self, + debugger: SBDebugger, + command: str, + exe_ctx: SBExecutionContext, + result: SBCommandReturnObject, + ) -> None: + """Handles the invocation of 'go get-type' command""" + args = self.parser.parse_args(shlex.split(command)) + address_or_name = args.target + depth: int = args.depth + + self.context_handler.refresh(exe_ctx) + + if self.settings.go_support_level == "disable": + print_message(MSG_TYPE.ERROR, GO_DISABLED_MSG) + elif not go_context_analysis(self.settings): + print_message(MSG_TYPE.ERROR, "The binary does not appear to be a Go binary.") + elif LLEFState.go_state.moduledata_info is None: + print_message(MSG_TYPE.ERROR, "No type information available in this Go binary.") + else: + # At this point, we're good to go with running the command. + + if address_or_name is None: + # Print a table + output_line("TYPE_POINTER - SHORT_NAME = DECONSTRUCTED_TYPE") + for ptr, type_struct in LLEFState.go_state.moduledata_info.type_structs.items(): + output_line(f"{hex(ptr)} - {type_struct.header.name} = {type_struct.get_underlying_type(depth)}") + else: + + try: + # User has typed in a numeric address + address = int(address_or_name, 0) + type_lookup = LLEFState.go_state.moduledata_info.type_structs.get(address) + if type_lookup is not None: + output_line( + f"{hex(address)} - {type_lookup.header.name} = {type_lookup.get_underlying_type(depth)}", + never_truncate=True, + ) + output_line(f"Size in bytes: {hex(type_lookup.header.size)}") + else: + print_message( + MSG_TYPE.ERROR, f"Could not find type information struct at address {hex(address)}" + ) + + except ValueError: + # User has typed in a string name + name = address_or_name + + if self.type_getter is None: + self.type_getter = TypeGetter(LLEFState.go_state.moduledata_info.type_structs) + + parsed_type = self.type_getter.string_to_type(name) + if parsed_type is not None: + output_line(f"{name} = {parsed_type.get_underlying_type(depth)}", never_truncate=True) + output_line(f"Size in bytes: {hex(parsed_type.header.size)}") + else: + print_message(MSG_TYPE.ERROR, f"Could not parse type '{name}'") + + found = False + for ptr, type_struct in LLEFState.go_state.moduledata_info.type_structs.items(): + if name in type_struct.header.name: + if found is False: + print_message(MSG_TYPE.ERROR, "Did you mean:") + found = True + output_line(f"{hex(ptr)} - {type_struct.header.name}") + + +class GolangUnpackTypeCommand(BaseCommand): + """Implements the 'unpack-type' subcommand""" + + program: str = "unpack-type" + container: type[BaseContainer] = GolangContainer + parser: argparse.ArgumentParser + context_handler: ContextHandler + settings: LLEFSettings + + type_getter: Union[TypeGetter, None] + + @classmethod + def get_command_parser(cls) -> argparse.ArgumentParser: + """Get the command parser.""" + parser = argparse.ArgumentParser() + parser.add_argument( + "address", + type=hex_int, + help="The pointer to the start of this data structure", + ) + parser.add_argument( + "type", + help="Either a type information structure address or a type name", + ) + parser.add_argument( + "-d", + "--depth", + type=positive_int, + default=GO_DEFAULT_UNPACK_DEPTH, + help=f"Depth to unpack child objects, default is {GO_DEFAULT_UNPACK_DEPTH}", + ) + return parser + + @staticmethod + def get_short_help() -> str: + return "Usage: go unpack-type address type [--depth n]" + + @staticmethod + def get_long_help() -> str: + return ( + "Unpacks a Go object at an address using supplied type information." + + os.linesep + + "The type can either be a string or a pointer to a type information structure." + + os.linesep + + "The depth argument specifies how deeply to follow and unpack child objects. " + + f"It defaults to {GO_DEFAULT_UNPACK_DEPTH}" + + os.linesep + + GolangUnpackTypeCommand.get_command_parser().format_help() + ) + + def __init__(self, debugger: SBDebugger, _: dict[Any, Any]) -> None: + self.parser = self.get_command_parser() + self.context_handler = ContextHandler(debugger) + self.settings = LLEFSettings(debugger) + + self.type_getter = None # For now, and will be set when we have a context later. + + @check_version("15.0.0") + @check_process + def __call__( + self, + debugger: SBDebugger, + command: str, + exe_ctx: SBExecutionContext, + result: SBCommandReturnObject, + ) -> None: + """Handles the invocation of 'go unpack-type' command""" + args = self.parser.parse_args(shlex.split(command)) + object_pointer: int = args.address + type_name_or_pointer: str = args.type + depth: int = args.depth + + self.context_handler.refresh(exe_ctx) + + if self.settings.go_support_level == "disable": + print_message(MSG_TYPE.ERROR, GO_DISABLED_MSG) + elif not go_context_analysis(self.settings): + print_message(MSG_TYPE.ERROR, "The binary does not appear to be a Go binary.") + elif LLEFState.go_state.moduledata_info is None: + print_message(MSG_TYPE.ERROR, "No type information available in this Go binary.") + else: + # At this point, we're good to go with running the command. + + # First, decode the type struct. + type_struct = None + try: + # User has typed in a numeric address for type pointer + type_ptr = int(type_name_or_pointer, 0) + type_struct = LLEFState.go_state.moduledata_info.type_structs.get(type_ptr) + if type_struct is None: + print_message(MSG_TYPE.ERROR, f"Could not find type information struct at address {hex(type_ptr)}") + + except ValueError: + # User has typed in a string name + name = type_name_or_pointer + + if self.type_getter is None: + self.type_getter = TypeGetter(LLEFState.go_state.moduledata_info.type_structs) + + type_struct = self.type_getter.string_to_type(name) + if type_struct is None: + print_message(MSG_TYPE.ERROR, f"Could not parse type '{name}'") + + # Now, unpack the object. + if type_struct is not None: + info = ExtractInfo( + proc=exe_ctx.GetProcess(), + ptr_size=LLEFState.go_state.pclntab_info.ptr_size, + type_structs=LLEFState.go_state.moduledata_info.type_structs, + ) + py_obj = type_struct.extract_at(info, object_pointer, set(), depth) + + if isinstance(py_obj, GoDataBad): + # Print a custom error message, otherwise the user would just see "?" + err = "Couldn't unpack from that address (impossible data or non-existent memory)." + print_message(MSG_TYPE.ERROR, err) + else: + # This can span multiple lines of output, as it's normally used to get full information + # after a truncated version was displayed inline in the context viewer. + output_line(str(py_obj), never_truncate=True) + + +class GolangBacktraceCommand(BaseCommand): + """Implements the 'backtrace' subcommand""" + + program: str = "backtrace" + container: type[BaseContainer] = GolangContainer + parser: argparse.ArgumentParser + context_handler: ContextHandler + settings: LLEFSettings + + @classmethod + def get_command_parser(cls) -> argparse.ArgumentParser: + """Get the command parser.""" + parser = argparse.ArgumentParser() + parser.add_argument( + "-d", + "--depth", + type=positive_int, + default=10, + help="The number of lines of backtrace to display, default is 10", + ) + return parser + + @staticmethod + def get_short_help() -> str: + return "Usage: go backtrace [--depth n]" + + @staticmethod + def get_long_help() -> str: + return ( + "Displays a backtrace from the current function up the call stack." + + os.linesep + + "The depth argument specifies how many functions to traverse back. The default is 10." + + os.linesep + + GolangBacktraceCommand.get_command_parser().format_help() + ) + + def __init__(self, debugger: SBDebugger, _: dict[Any, Any]) -> None: + self.parser = self.get_command_parser() + self.context_handler = ContextHandler(debugger) + self.settings = LLEFSettings(debugger) + + @check_version("15.0.0") + @check_process + def __call__( + self, + debugger: SBDebugger, + command: str, + exe_ctx: SBExecutionContext, + result: SBCommandReturnObject, + ) -> None: + """Handles the invocation of 'go backtrace' command""" + args = self.parser.parse_args(shlex.split(command)) + depth: int = args.depth + + self.context_handler.refresh(exe_ctx) + + if self.settings.go_support_level == "disable": + print_message(MSG_TYPE.ERROR, GO_DISABLED_MSG) + elif not go_context_analysis(self.settings): + print_message(MSG_TYPE.ERROR, "The binary does not appear to be a Go binary.") + else: + # At this point, we're good to go with running the command. + + backtrace = go_get_backtrace( + exe_ctx.GetProcess(), + exe_ctx.GetFrame(), + self.context_handler.arch, + self.context_handler.color_settings, + depth, + ) + if backtrace is not None: + output_line(backtrace) + else: + print_message( + MSG_TYPE.ERROR, + "Go traceback failed (only supported on x86 and x86_64). Try using LLDB's `bt` command.", + ) + + +class GolangReanalyseCommand(BaseCommand): + """Implements the 'reanalyse' subcommand""" + + program: str = "reanalyse" + container: type[BaseContainer] = GolangContainer + parser: argparse.ArgumentParser + context_handler: ContextHandler + settings: LLEFSettings + + @classmethod + def get_command_parser(cls) -> argparse.ArgumentParser: + """Get the command parser.""" + parser = argparse.ArgumentParser() + return parser + + @staticmethod + def get_short_help() -> str: + return "Usage: go reanalyse" + + @staticmethod + def get_long_help() -> str: + return ( + "Clears the internal Go-specific analysis and performs it again according to the support level." + + os.linesep + + "'auto' - performs most analysis." + + os.linesep + + "'force' - includes heavier analysis such as scanning Windows binaries to detect Go." + + os.linesep + + GolangReanalyseCommand.get_command_parser().format_help() + ) + + def __init__(self, debugger: SBDebugger, _: dict[Any, Any]) -> None: + self.parser = self.get_command_parser() + self.context_handler = ContextHandler(debugger) + self.settings = LLEFSettings(debugger) + + @check_version("15.0.0") + @check_process + def __call__( + self, + debugger: SBDebugger, + command: str, + exe_ctx: SBExecutionContext, + result: SBCommandReturnObject, + ) -> None: + """Handles the invocation of 'reanalyse' command""" + _ = self.parser.parse_args(shlex.split(command)) + + self.context_handler.refresh(exe_ctx) + + if self.settings.go_support_level == "disable": + print_message(MSG_TYPE.ERROR, GO_DISABLED_MSG) + else: + LLEFState.go_state = GoState() + setup_go(exe_ctx.GetProcess(), exe_ctx.GetTarget(), self.settings) + go_calculate_base_pointer.cache_clear() + go_find_func.cache_clear() diff --git a/commands/hexdump.py b/commands/hexdump.py index e70e316..9661414 100644 --- a/commands/hexdump.py +++ b/commands/hexdump.py @@ -2,7 +2,7 @@ import argparse import shlex -from typing import Any, Dict +from typing import Any, Union from lldb import SBCommandReturnObject, SBDebugger, SBExecutionContext @@ -17,13 +17,13 @@ class HexdumpCommand(BaseCommand): program: str = "hexdump" container = None - context_handler = None + context_handler: Union[ContextHandler, None] = None # Define alias set, where each entry is an alias with any arguments the command should take. # For example, 'dq' maps to 'hexdump qword'. alias_set = {"dq": "qword", "dd": "dword", "dw": "word", "db": "byte"} - def __init__(self, debugger: SBDebugger, __: Dict[Any, Any]) -> None: + def __init__(self, debugger: SBDebugger, __: dict[Any, Any]) -> None: super().__init__() self.parser = self.get_command_parser() self.context_handler = ContextHandler(debugger) @@ -83,6 +83,9 @@ def __call__( address = args.address size = args.size + if self.context_handler is None: + raise AttributeError("Class not properly initialised: self.context_handler is None") + self.context_handler.refresh(exe_ctx) start = (size - 1) * divisions if args.reverse else 0 diff --git a/commands/pattern.py b/commands/pattern.py index 013a2c0..9e0fa12 100644 --- a/commands/pattern.py +++ b/commands/pattern.py @@ -4,7 +4,7 @@ import binascii import os import shlex -from typing import Any, Dict, Type +from typing import Any from lldb import SBCommandReturnObject, SBDebugger, SBExecutionContext @@ -38,7 +38,7 @@ class PatternCreateCommand(BaseCommand): """Implements the 'create' subcommand""" program: str = "create" - container: Type[BaseContainer] = PatternContainer + container: type[BaseContainer] = PatternContainer state: LLEFState @classmethod @@ -66,7 +66,7 @@ def get_long_help() -> str: + PatternCreateCommand.get_command_parser().format_help() ) - def __init__(self, _: SBDebugger, __: Dict[Any, Any]) -> None: + def __init__(self, _: SBDebugger, __: dict[Any, Any]) -> None: """Class initializer.""" self.parser = self.get_command_parser() self.state = LLEFState() @@ -112,7 +112,7 @@ class PatternSearchCommand(BaseCommand): """Implements the 'search' subcommand.""" program = "search" - container: Type[BaseContainer] = PatternContainer + container: type[BaseContainer] = PatternContainer state: LLEFState @classmethod @@ -134,7 +134,7 @@ def get_long_help() -> str: + PatternCreateCommand.get_command_parser().format_help() ) - def __init__(self, _: SBDebugger, __: Dict[Any, Any]) -> None: + def __init__(self, _: SBDebugger, __: dict[Any, Any]) -> None: """Class initializer.""" self.parser = self.get_command_parser() self.state = LLEFState() diff --git a/commands/scan.py b/commands/scan.py index dd82ed5..539697d 100644 --- a/commands/scan.py +++ b/commands/scan.py @@ -2,9 +2,18 @@ import argparse import shlex -from typing import Any, Dict, List, Tuple - -from lldb import SBCommandReturnObject, SBDebugger, SBError, SBExecutionContext, SBMemoryRegionInfo, SBProcess, SBTarget +from typing import Any, Union + +from lldb import ( + SBCommandReturnObject, + SBDebugger, + SBError, + SBExecutionContext, + SBMemoryRegionInfo, + SBProcess, + SBTarget, + SBValue, +) from commands.base_command import BaseCommand from common.constants import MSG_TYPE @@ -19,9 +28,9 @@ class ScanCommand(BaseCommand): program: str = "scan" container = None - context_handler = None + context_handler: Union[ContextHandler, None] = None - def __init__(self, debugger: SBDebugger, __: Dict[Any, Any]) -> None: + def __init__(self, debugger: SBDebugger, __: dict[Any, Any]) -> None: super().__init__() self.parser = self.get_command_parser() self.context_handler = ContextHandler(debugger) @@ -53,7 +62,7 @@ def get_long_help() -> str: """Return a longer help message""" return ScanCommand.get_command_parser().format_help() - def parse_address_ranges(self, process: SBProcess, region_name: str) -> List[Tuple[int, int]]: + def parse_address_ranges(self, process: SBProcess, region_name: str) -> list[tuple[int, int]]: """ Parse a custom address range (e.g., 0x7fffffffe208-0x7fffffffe240) or extract address ranges from memory regions with a given name (e.g., libc). @@ -78,7 +87,7 @@ def parse_address_ranges(self, process: SBProcess, region_name: str) -> List[Tup return address_ranges - def find_address_ranges(self, process: SBProcess, region_name: str) -> List[Tuple[int, int]]: + def find_address_ranges(self, process: SBProcess, region_name: str) -> list[tuple[int, int]]: """ Extract address ranges from memory regions with @region_name. @@ -107,12 +116,12 @@ def find_address_ranges(self, process: SBProcess, region_name: str) -> List[Tupl def scan( self, - search_address_ranges: List[Tuple[int, int]], - target_address_ranges: List[Tuple[int, int]], + search_address_ranges: list[tuple[int, int]], + target_address_ranges: list[tuple[int, int]], address_size: int, process: SBProcess, target: SBTarget, - ) -> List[Tuple[int, int]]: + ) -> list[tuple[SBValue, int]]: """ Scan through a given search space in memory for addresses that point towards a target memory space. @@ -153,6 +162,9 @@ def __call__( search_region = args.search_region target_region = args.target_region + if self.context_handler is None: + raise AttributeError("Class not properly initialised: self.context_handler is None") + self.context_handler.refresh(exe_ctx) search_address_ranges = self.parse_address_ranges(exe_ctx.process, search_region) @@ -171,4 +183,4 @@ def __call__( results = self.scan(search_address_ranges, target_address_ranges, address_size, exe_ctx.process, exe_ctx.target) for address, offset in results: - self.context_handler.print_stack_addr(address, offset) + self.context_handler.print_stack_addr(address.GetValueAsUnsigned(), offset) diff --git a/commands/settings.py b/commands/settings.py index 82cfc26..fdbdd19 100644 --- a/commands/settings.py +++ b/commands/settings.py @@ -1,7 +1,7 @@ """llefsettings command class.""" import argparse -from typing import Any, Dict +from typing import Any from lldb import SBDebugger @@ -15,7 +15,7 @@ class SettingsCommand(BaseSettingsCommand): program: str = "llefsettings" container = None - def __init__(self, debugger: SBDebugger, dictionary: Dict[Any, Any]) -> None: + def __init__(self, debugger: SBDebugger, dictionary: dict[Any, Any]) -> None: super().__init__(debugger, dictionary) self.settings = LLEFSettings(debugger) diff --git a/commands/xinfo.py b/commands/xinfo.py index 9493a8c..8b60cb6 100644 --- a/commands/xinfo.py +++ b/commands/xinfo.py @@ -3,7 +3,7 @@ import argparse import os import shlex -from typing import Any, Dict +from typing import Any, Union from lldb import ( SBCommandReturnObject, @@ -31,7 +31,7 @@ class XinfoCommand(BaseCommand): container = None context_handler = None - def __init__(self, debugger: SBDebugger, __: Dict[Any, Any]) -> None: + def __init__(self, debugger: SBDebugger, __: dict[Any, Any]) -> None: super().__init__() self.parser = self.get_command_parser() self.context_handler = ContextHandler(debugger) @@ -58,7 +58,7 @@ def get_long_help() -> str: """Return a longer help message""" return XinfoCommand.get_command_parser().format_help() - def get_xinfo(self, process: SBProcess, target: SBTarget, address: int) -> Dict[str, Any]: + def get_xinfo(self, process: SBProcess, target: SBTarget, address: int) -> Union[dict[XINFO, Any], None]: """ Gets memory region information for a given `address`, including: - `region_start` address @@ -81,7 +81,7 @@ def get_xinfo(self, process: SBProcess, target: SBTarget, address: int) -> Dict[ if error.Fail() or not memory_region.IsMapped(): return None - xinfo = { + xinfo: dict[XINFO, Any] = { XINFO.REGION_START: None, XINFO.REGION_END: None, XINFO.REGION_SIZE: None, @@ -132,7 +132,7 @@ def __call__( args = self.parser.parse_args(shlex.split(command)) address = args.address - if address < 0 or address > 2 ** get_arch(exe_ctx.target).bits: + if address < 0 or address > 2 ** get_arch(exe_ctx.target)().bits: print_message(MSG_TYPE.ERROR, "Invalid address.") return diff --git a/common/base_settings.py b/common/base_settings.py index 795ad46..dc87037 100644 --- a/common/base_settings.py +++ b/common/base_settings.py @@ -20,27 +20,27 @@ class BaseLLEFSettings(metaclass=Singleton): _RAW_CONFIG: configparser.ConfigParser = configparser.ConfigParser() @classmethod - def _get_setting_names(cls): + def _get_setting_names(cls) -> list[str]: return [name for name, value in vars(cls).items() if isinstance(value, property)] - def __init__(self): + def __init__(self) -> None: self.state = LLEFState() self.load() @abstractmethod - def validate_settings(self, setting=None) -> bool: + def validate_settings(self, setting: str = "") -> bool: """ Validate settings """ - def load_default_settings(self): + def load_default_settings(self) -> None: """ Reset settings and use default values """ self._RAW_CONFIG = configparser.ConfigParser() self._RAW_CONFIG.add_section(self.GLOBAL_SECTION) - def load(self, reset=False): + def load(self, reset: bool = False) -> None: """ Load settings from file """ @@ -63,7 +63,7 @@ def load(self, reset=False): self.load_default_settings() output_line("Error parsing config. Default settings loaded.") - def list(self): + def list_settings(self) -> None: """ List all settings and their current values """ @@ -71,14 +71,14 @@ def list(self): for setting_name in settings_names: output_line(f"{setting_name}={getattr(self, setting_name)}") - def save(self): + def save(self) -> None: """ Save LLEF setting to file defined in `LLEF_CONFIG_PATH` """ with open(self.LLEF_CONFIG_PATH, "w") as configfile: self._RAW_CONFIG.write(configfile) - def set(self, setting: str, value: str): + def set(self, setting: str, value: str) -> None: """ Set a LLEF setting """ diff --git a/common/color_settings.py b/common/color_settings.py index f4728fb..89ee286 100644 --- a/common/color_settings.py +++ b/common/color_settings.py @@ -1,7 +1,6 @@ """Color settings module""" import os -from typing import List from common.base_settings import BaseLLEFSettings from common.constants import TERM_COLORS @@ -17,94 +16,98 @@ class LLEFColorSettings(BaseLLEFSettings, metaclass=Singleton): LLEF_CONFIG_PATH = os.path.join(os.path.expanduser("~"), ".llef_colors") GLOBAL_SECTION = "LLEF" - supported_colors: List[str] = [] + supported_colors: list[str] = [] @property - def register_color(self): + def register_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "register_color", fallback="BLUE").upper() @property - def modified_register_color(self): + def modified_register_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "modified_register_color", fallback="RED").upper() @property - def code_color(self): + def code_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "code_color", fallback="RED").upper() @property - def heap_color(self): + def heap_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "heap_color", fallback="GREEN").upper() @property - def stack_color(self): + def stack_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "stack_color", fallback="PINK").upper() @property - def string_color(self): + def string_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "string_color", fallback="YELLOW").upper() @property - def stack_address_color(self): + def stack_address_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "stack_address_color", fallback="CYAN").upper() @property - def function_name_color(self): + def function_name_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "function_name_color", fallback="GREEN").upper() @property - def instruction_color(self): + def instruction_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "instruction_color", fallback="GREY").upper() @property - def highlighted_instruction_color(self): + def highlighted_instruction_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "highlighted_instruction_color", fallback="GREEN").upper() @property - def line_color(self): + def line_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "line_color", fallback="GREY").upper() @property - def rebased_address_color(self): + def rebased_address_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "rebased_address_color", fallback="GREY").upper() @property - def section_header_color(self): + def section_header_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "section_header_color", fallback="BLUE").upper() @property - def highlighted_index_color(self): + def highlighted_index_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "highlighted_index_color", fallback="GREEN").upper() @property - def index_color(self): + def index_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "index_color", fallback="PINK").upper() @property - def dereferenced_value_color(self): + def dereferenced_value_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "dereferenced_value_color", fallback="GREY").upper() @property - def dereferenced_register_color(self): + def dereferenced_register_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "dereferenced_register_color", fallback="BLUE").upper() @property - def frame_argument_name_color(self): + def frame_argument_name_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "frame_argument_name_color", fallback="YELLOW").upper() @property - def read_memory_address_color(self): + def read_memory_address_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "read_memory_address_color", fallback="CYAN").upper() @property - def address_operand_color(self): + def address_operand_color(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "address_operand_color", fallback="RED").upper() - def __init__(self): + @property + def go_type_color(self) -> str: + return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "go_type_color", fallback="CYAN").upper() + + def __init__(self) -> None: self.supported_colors = [color.name for color in TERM_COLORS] self.supported_colors.remove(TERM_COLORS.ENDC.name) super().__init__() - def validate_settings(self, setting=None) -> bool: + def validate_settings(self, setting: str = "") -> bool: """ Validate settings by attempting to retrieve all properties thus executing any ConfigParser coverters Check all colors are valid options @@ -129,7 +132,7 @@ def validate_settings(self, setting=None) -> bool: output_line(f"Error parsing setting {setting_name}. Invalid value '{raw_value}'") return valid - def list(self): + def list_settings(self) -> None: """ List all color settings and their current values, colored appropriately """ diff --git a/common/constants.py b/common/constants.py index aaf6531..c1f75ed 100644 --- a/common/constants.py +++ b/common/constants.py @@ -2,6 +2,10 @@ from enum import Enum, IntEnum +# Pointers are stored using int, however, this can lead to confusion between an offset, an absolute address, +# and just a plain old integer value. The llef_pointer alias is intended to improve readability. +pointer = int + class TERM_COLORS(Enum): """Used to colorify terminal output.""" diff --git a/common/context_handler.py b/common/context_handler.py index 0bfb53c..800818b 100644 --- a/common/context_handler.py +++ b/common/context_handler.py @@ -1,6 +1,5 @@ -import os from string import printable -from typing import List, Optional, Tuple, Type +from typing import Union from lldb import ( SBAddress, @@ -10,18 +9,34 @@ SBExecutionContext, SBFrame, SBMemoryRegionInfo, + SBMemoryRegionInfoList, SBProcess, SBTarget, SBThread, SBValue, + debugger, ) from arch import get_arch, get_arch_from_str from arch.base_arch import BaseArch, FlagRegister from common.color_settings import LLEFColorSettings from common.constants import GLYPHS, TERM_COLORS +from common.golang.analysis import ( + go_annotate_pointer_line, + go_get_backtrace, + go_get_function_from_pc, + go_stop_hook, +) +from common.golang.util import go_context_analysis, is_address_go_frame_pointer from common.instruction_util import extract_instructions, print_instruction, print_instructions -from common.output_util import clear_page, color_string, output_line, print_line, print_line_with_string +from common.output_util import ( + clear_page, + color_string, + generate_rebased_address_string, + output_line, + print_line, + print_line_with_string, +) from common.settings import LLEFSettings from common.state import LLEFState from common.util import ( @@ -31,7 +46,9 @@ find_stack_regions, get_frame_arguments, get_frame_range, + get_function_info_from_frame, get_registers, + hex_or_str, is_code, is_heap, is_stack, @@ -45,14 +62,14 @@ class ContextHandler: process: SBProcess target: SBTarget thread: SBThread - arch: Type[BaseArch] + arch: type[BaseArch] debugger: SBDebugger - exe_ctx: SBExecutionContext settings: LLEFSettings color_settings: LLEFColorSettings + regions: Union[SBMemoryRegionInfoList, None] state: LLEFState - darwin_stack_regions: List[SBMemoryRegionInfo] - darwin_heap_regions: List[Tuple[int, int]] + darwin_stack_regions: list[SBMemoryRegionInfo] + darwin_heap_regions: Union[list[tuple[int, int]], None] def __init__( self, @@ -66,21 +83,11 @@ def __init__( self.color_settings = LLEFColorSettings() self.state = LLEFState() self.state.change_use_color(self.settings.color_output) - self.darwin_stack_regions = None + self.darwin_stack_regions = [] self.darwin_heap_regions = None - def generate_rebased_address_string(self, address: SBAddress) -> str: - module = address.GetModule() - - if module is not None and self.settings.rebase_addresses is True: - file_name = os.path.basename(str(module.file)) - rebased_address = address.GetFileAddress() + self.settings.rebase_offset - return color_string(f"({file_name} {rebased_address:#x})", self.color_settings.rebased_address_color) - - return "" - def generate_printable_line_from_pointer( - self, pointer: SBValue, address_containing_pointer: Optional[int] = None + self, pointer: int, address_containing_pointer: Union[int, None] = None ) -> str: """ Generate a line from a memory address (@pointer) that contains relevant @@ -91,55 +98,80 @@ def generate_printable_line_from_pointer( line = "" pointer_value = SBAddress(pointer, self.target) - if pointer_value.symbol.IsValid(): - offset = pointer_value.offset - pointer_value.symbol.GetStartAddress().offset - line += f" {self.generate_rebased_address_string(pointer_value)} {GLYPHS.RIGHT_ARROW.value}" - line += color_string( - f"<{pointer_value.symbol.name}+{offset}>", self.color_settings.dereferenced_value_color + # Check if LLEF can perform Go-specific analysis. + if go_context_analysis(self.settings): + line = go_annotate_pointer_line( + self.process, + self.target, + self.regions, + pointer, + address_containing_pointer, + self.settings, + self.color_settings, ) - referenced_string = attempt_to_read_string_from_memory(self.process, pointer_value.GetLoadAddress(self.target)) + # Perform generic analysis if there is no Go analysis string. + if line == "": + if pointer_value.symbol.IsValid(): + offset = pointer_value.offset - pointer_value.symbol.GetStartAddress().offset + rebased_address = generate_rebased_address_string( + pointer_value, + self.settings.rebase_addresses, + self.settings.rebase_offset, + self.color_settings.rebased_address_color, + ) + line += f" {rebased_address} {GLYPHS.RIGHT_ARROW.value}" + line += color_string( + f"<{pointer_value.symbol.name}+{offset}>", self.color_settings.dereferenced_value_color + ) - if len(referenced_string) > 0 and referenced_string.isprintable(): - # Only add this to the line if there are any printable characters in refd_string - referenced_string = referenced_string.replace("\n", " ") - line += color_string( - referenced_string, self.color_settings.string_color, f' {GLYPHS.RIGHT_ARROW.value} ("', "?)" + referenced_string = attempt_to_read_string_from_memory( + self.process, pointer_value.GetLoadAddress(self.target) ) + if len(referenced_string) > 0 and referenced_string.isprintable(): + # Only add this to the line if all characters in referenced_string are printable. + referenced_string = referenced_string.replace("\n", " ") + line += color_string( + referenced_string, self.color_settings.string_color, f' {GLYPHS.RIGHT_ARROW.value} ("', '"?)' + ) + if address_containing_pointer is not None: registers_pointing_to_address = [] for register in get_registers(self.frame, self.arch().gpr_key): if register.GetValueAsUnsigned() == address_containing_pointer: registers_pointing_to_address.append(f"${register.GetName()}") + + if is_address_go_frame_pointer(self.settings, address_containing_pointer, self.frame): + registers_pointing_to_address.append("(Go Frame Pointer)") if len(registers_pointing_to_address) > 0: reg_list = ", ".join(registers_pointing_to_address) line += color_string( f"{GLYPHS.LEFT_ARROW.value}{reg_list}", self.color_settings.dereferenced_register_color ) - return line - def print_stack_addr(self, addr: SBValue, offset: int) -> None: + def print_stack_addr(self, addr: int, offset: int) -> None: """Produce a printable line containing information about a given stack @addr and print it""" # Add stack address and offset to line line = color_string( - hex(addr.GetValueAsUnsigned()), + hex(addr), self.color_settings.stack_address_color, rwrap=f"{GLYPHS.VERTICAL_LINE.value}+{offset:04x}: ", ) # Add value to line - err = SBError() - stack_value = self.process.ReadPointerFromMemory(addr.GetValueAsUnsigned(), err) - if err.Success(): - line += f"0x{stack_value:0{self.arch().bits // 4}x}" - else: - # Shouldn't happen as stack should always contain something - line += str(err) + ptr_bits = self.arch().bits + max_valid_address = pow(2, ptr_bits) + + if addr >= 0 and addr <= max_valid_address: + err = SBError() + stack_value = self.process.ReadPointerFromMemory(addr, err) + if err.Success(): + line += f"0x{stack_value:0{ptr_bits // 4}x}" + line += self.generate_printable_line_from_pointer(stack_value, addr) - line += self.generate_printable_line_from_pointer(stack_value, addr.GetValueAsUnsigned()) output_line(line) def print_memory_address(self, addr: int, offset: int, size: int) -> None: @@ -155,7 +187,7 @@ def print_memory_address(self, addr: int, offset: int, size: int) -> None: err = SBError() memory_value = self.process.ReadMemory(addr, size, err) - if err.Success(): + if err.Success() and memory_value is not None: line += f"0x{int.from_bytes(memory_value, 'little'):0{size * 2}x}" else: line += str(err) @@ -170,8 +202,8 @@ def print_bytes(self, addr: int, size: int) -> None: # Add value to line err = SBError() - memory_value: bytes = self.process.ReadMemory(addr, size, err) - if err.Success(): + memory_value = self.process.ReadMemory(addr, size, err) + if err.Success() and memory_value is not None: line += f"{memory_value.hex(' '):47} " # Add characters to line @@ -275,16 +307,17 @@ def display_registers(self) -> None: for reg_set in self.frame.registers: for reg in reg_set: register_list.append(reg.name) - for reg in self.arch.flag_registers: + for reg in self.arch().flag_registers: if reg.name in register_list: register_list.remove(reg.name) else: register_list = self.arch().gpr_registers for reg in register_list: - if self.frame.register[reg] is not None: - self.print_register(self.frame.register[reg]) - for flag_register in self.arch.flag_registers: + register_value = self.frame.register[reg] + if register_value is not None: + self.print_register(register_value) + for flag_register in self.arch().flag_registers: if self.frame.register[flag_register.name] is not None: self.print_flags_register(flag_register) @@ -296,10 +329,11 @@ def display_stack(self) -> None: line_color=self.color_settings.line_color, string_color=self.color_settings.section_header_color, ) - for inc in range(0, self.arch().bits, 8): + + ptr_width = self.arch().bits // 8 + for inc in range(0, ptr_width * self.settings.stack_view_size, ptr_width): stack_pointer = self.frame.GetSP() - addr = self.target.EvaluateExpression(f"{stack_pointer} + {inc}") - self.print_stack_addr(addr, inc) + self.print_stack_addr(stack_pointer + inc, inc) def display_code(self) -> None: """ @@ -314,36 +348,57 @@ def display_code(self) -> None: pc = self.frame.GetPC() filename = address_to_filename(self.target, pc) - function_name = self.frame.GetFunctionName() - output_line(f"{filename}'{function_name}:") + function_name = self.frame.GetFunctionName() or "?" frame_start_address, frame_end_address = get_frame_range(self.frame, self.target) + function_start = frame_start_address + + if go_context_analysis(self.settings): + # Attempt to find a frame start and function name in Go PCLNTAB table. + function_start, function_name = go_get_function_from_pc(pc, function_start, function_name) - pre_instructions = extract_instructions(self.target, frame_start_address, pc - 1, self.state.disassembly_syntax) + output_line(f"{filename}'{function_name}:") + + pre_instructions = extract_instructions(self.target, function_start, pc - 1, self.state.disassembly_syntax)[-3:] print_instructions( self.target, - pre_instructions[-3:], + pre_instructions, frame_start_address, + function_start, + self.settings, self.color_settings, ) - post_instructions = extract_instructions(self.target, pc, frame_end_address, self.state.disassembly_syntax) + max_post_instructions = self.settings.max_disassembly_length - len(pre_instructions) + + # Limit disassembly length to prevent issues with very large functions. + max_disassembly_end_address = pc + (max_post_instructions * self.arch().max_instr_size) + 1 + disassembly_end_address = min(frame_end_address, max_disassembly_end_address) + + post_instructions = extract_instructions( + self.target, pc, disassembly_end_address, self.state.disassembly_syntax + ) if len(post_instructions) > 0: pc_instruction = post_instructions[0] + # Print instruction at program counter (with highlighting). print_instruction( self.target, pc_instruction, frame_start_address, + function_start, + self.settings, self.color_settings, True, ) - limit = 9 - min(len(pre_instructions), 3) + # Print remaining instructions. print_instructions( self.target, - post_instructions[1:limit], + post_instructions[1:max_post_instructions], frame_start_address, + function_start, + self.settings, self.color_settings, ) @@ -355,7 +410,34 @@ def display_threads(self) -> None: string_color=self.color_settings.section_header_color, ) for thread in self.process: - output_line(thread) + if not thread.IsValid(): + continue + + frame = thread.GetFrameAtIndex(0) + if frame is None or not frame.IsValid(): + continue + + function_name, func_offset = get_function_info_from_frame(self.settings, self.target, frame) + + base_name = "" + module = frame.GetModule() + if module is not None and module.IsValid(): + file = module.GetFileSpec() + if file is not None and file.IsValid(): + base_name = file.GetFilename() + + line = ( + f"thread #{thread.idx}: tid = {thread.id}, {hex_or_str(frame.pc)} " + f"{base_name}`{function_name} + {func_offset}" + ) + if thread.name: + line += f""", name = {color_string("'" + thread.name + "'", "GREEN")}""" + if thread.queue: + line += f""", queue = {color_string("'" + thread.queue + "'", "GREEN")}""" + stop_reason = thread.GetStopDescription(64) + if stop_reason: + line += f""", stop reason = {color_string(stop_reason, "RED")}""" + output_line(line) def display_trace(self) -> None: """ @@ -366,35 +448,42 @@ def display_trace(self) -> None: line_color=self.color_settings.line_color, string_color=self.color_settings.section_header_color, ) + length = self.settings.max_trace_length - for i in range(self.thread.GetNumFrames()): - if i == 0: - number_color = self.color_settings.highlighted_index_color - else: - number_color = self.color_settings.index_color - line = color_string(f"#{i}", number_color, "[", "]") - - current_frame = self.thread.GetFrameAtIndex(i) - pc_address = current_frame.GetPCAddress() - func = current_frame.GetFunction() - trace_address = pc_address.GetLoadAddress(self.target) - - if func: - line += ( - f"{trace_address:#x}{self.generate_rebased_address_string(pc_address)} {GLYPHS.RIGHT_ARROW.value} " - f"{color_string(func.GetName(), self.color_settings.function_name_color)}" - ) - else: - line += ( - f"{trace_address:#x}{self.generate_rebased_address_string(pc_address)} {GLYPHS.RIGHT_ARROW.value} " - f"{color_string(current_frame.GetSymbol().GetName(), self.color_settings.function_name_color)}" + line = "" + if go_context_analysis(self.settings): + go_backtrace = go_get_backtrace(self.process, self.frame, self.arch, self.color_settings, length) + if go_backtrace is not None: + line = go_backtrace + + # Fallback to generic stack unwind. + if line == "": + for i in range(min(self.thread.GetNumFrames(), length)): + if i == 0: + number_color = self.color_settings.highlighted_index_color + else: + number_color = self.color_settings.index_color + line = color_string(f"#{i}", number_color, "[", "]") + + current_frame = self.thread.GetFrameAtIndex(i) + pc_address = current_frame.GetPCAddress() + trace_address = pc_address.GetLoadAddress(self.target) + + function_name, _ = get_function_info_from_frame(self.settings, self.target, current_frame) + rebased_address = generate_rebased_address_string( + pc_address, + self.settings.rebase_addresses, + self.settings.rebase_offset, + self.color_settings.rebased_address_color, ) + line += f"{trace_address:#x}{rebased_address} {GLYPHS.RIGHT_ARROW.value} " + line += f"{color_string(function_name, self.color_settings.function_name_color)}" - line += get_frame_arguments( - current_frame, frame_argument_name_color=TERM_COLORS[self.color_settings.frame_argument_name_color] - ) + line += get_frame_arguments( + current_frame, frame_argument_name_color=TERM_COLORS[self.color_settings.frame_argument_name_color] + ) - output_line(line) + output_line(line) def load_disassembly_syntax(self, debugger: SBDebugger) -> None: """Load the disassembly flavour from LLDB into LLEF's state.""" @@ -440,6 +529,9 @@ def refresh(self, exe_ctx: SBExecutionContext) -> None: # scanning method to be used. self.darwin_heap_regions = None + if self.settings.go_support_level != "disable": + go_stop_hook(exe_ctx, self.arch(), self.settings, debugger) + def display_context(self, exe_ctx: SBExecutionContext, update_registers: bool) -> None: """For up to date documentation on args provided to this function run: `help target stop-hook add`""" diff --git a/common/de_bruijn.py b/common/de_bruijn.py index 1f05102..9377873 100644 --- a/common/de_bruijn.py +++ b/common/de_bruijn.py @@ -1,9 +1,10 @@ """De Bruijn sequence utilities.""" import itertools +from collections.abc import Iterator -def de_bruijn(alphabet: str, n: int) -> str: +def de_bruijn(alphabet: bytearray, n: int) -> Iterator[int]: """ Generate De Bruijn sequence for alphabet and subsequences of length n (for compatibility. w/ pwnlib). Taken from GEF gef.py L3728 (2022.06). @@ -12,7 +13,7 @@ def de_bruijn(alphabet: str, n: int) -> str: k = len(alphabet) a = [0] * k * n - def db(t, p): + def db(t: int, p: int) -> Iterator[int]: if t > n: if n % p == 0: for j in range(1, p + 1): diff --git a/common/golang/analysis.py b/common/golang/analysis.py new file mode 100644 index 0000000..2f22f81 --- /dev/null +++ b/common/golang/analysis.py @@ -0,0 +1,481 @@ +"""Functions called in Go-mode that improve context output, usually by adding function names or types.""" + +import re +from typing import Union + +from lldb import ( + SBAddress, + SBDebugger, + SBError, + SBExecutionContext, + SBFrame, + SBInstruction, + SBMemoryRegionInfoList, + SBProcess, + SBTarget, + eInstructionControlFlowKindCall, + eInstructionControlFlowKindCondJump, + eInstructionControlFlowKindJump, +) + +from arch.base_arch import BaseArch +from common.color_settings import LLEFColorSettings +from common.constants import GLYPHS, pointer +from common.golang.constants import ( + GO_MIN_PTR, + GO_OBJECT_UNPACK_DEPTH, + GO_TYPE_DECODE_DEPTH, +) +from common.golang.data import Confidence +from common.golang.static import setup_go +from common.golang.types import ExtractInfo, GoType +from common.golang.util import ( + bytes_for_saved_pc, + get_arg_registers, + go_context_analysis, + go_find_func, + go_find_func_name_offset, + go_stackwalk, +) +from common.output_util import color_string, generate_rebased_address_string +from common.settings import LLEFSettings +from common.state import LLEFState +from common.util import is_code + + +def go_get_backtrace( + proc: SBProcess, frame: SBFrame, arch: type[BaseArch], col: LLEFColorSettings, length: int +) -> Union[str, None]: + """ + A Go-specific replacement for display_trace() in context_handler.py. + + :param SBProcess proc: The process object currently being debugged. + :param int my_pc: The program counter indicating the location we've stopped. + :param int sp: The current stack pointer. + :param type[BaseArch] arch: Information about the architecture of the target. + :param LLEFColorSettings col: Colour settings for output. + :return Union[str, None]: Return backtrace as a string, or None if unable to unwind Go stack. + """ + # Not all architectures are supported for backtrace. Currently, only x86 and x86_64 are supported. + output = "" + walk = go_stackwalk(proc, frame.GetPC(), frame.GetSP(), bytes_for_saved_pc(arch), length) + if len(walk) > 0: + + for index, (pc, bp) in enumerate(walk): + name, _ = go_find_func_name_offset(pc) + if name: + if index == 0: + number_color = col.highlighted_index_color + else: + number_color = col.index_color + line = color_string(f"#{index}", number_color, "[", "]") + + line += f"{pc:#x} " + line += color_string(f"(fp={bp:#x})", col.rebased_address_color) + line += f" {GLYPHS.RIGHT_ARROW.value} {color_string(name, col.function_name_color)}" + + output += line + "\n" + return output.rstrip() + return None + + +def go_annotate_jumps(target: SBTarget, instruction: SBInstruction, lldb_frame_start: pointer, comment: str) -> str: + """ + Annotates jump instructions with (if not already present) symbols. + + :param SBTarget target: The target context, currently being debugged. + :param SBInstruction instruction: The particular instruction to analyse. + :param int lldb_frame_start: This is where LLDB thinks the frame starts (a load address as per get_frame_range). + :param str comment: The current comment LLDB gives to this instruction. + :return str: Returns a new comment if able to find jump target information, otherwise the current comment. + """ + new_comment = comment + if instruction.GetControlFlowKind(target) in [ + eInstructionControlFlowKindCall, + eInstructionControlFlowKindJump, + eInstructionControlFlowKindCondJump, + ]: + jump_address: Union[int, None] = None + + if comment.startswith("<+"): + try: + jump_address = lldb_frame_start + int(comment[2:-1]) + except ValueError: + pass + + if jump_address is None: + # Try pulling the address directly from the instruction. + try: + jump_address = int(instruction.GetOperands(target), 0) + except ValueError: + pass + + if jump_address is not None: + jump_name, jump_offset = go_find_func_name_offset(jump_address) + if jump_name: + new_comment = jump_name + if jump_offset != 0: + new_comment += f" + {jump_offset}" + + return new_comment + + +def go_get_function_from_pc(pc: pointer, function_start: pointer, function_name: str) -> tuple[pointer, str]: + """ + Attempt to match a recovered Go function symbol and address for pc. + + :param int pc: Code address (file offset) to search function ranges for. + :param str function_name: The currently attributed symbol. + :param int function_start: The currently attributed base address for that symbol. + :return tuple[str, int]: An improved name/base pair, otherwise the provided function_name and function_start. + """ + record = go_find_func(pc) + if record is not None: + (entry, gofunc) = record + return (entry, gofunc.name) + return (function_start, function_name) + + +def is_sufficient_confidence(q: Confidence, settings: LLEFSettings) -> bool: + """ + Determines if the confidence of the unpacked object meets the user's requirement for it to be shown. + + :param Confidence q: The confidence of the unpacked object. + :param LLEFSettings settings: Settings object used for accessing the confidence threshold. + :return bool: Returns True if the object should be shown. + """ + threshold = settings.go_confidence_threshold + if threshold == "high" and (q is Confidence.CERTAIN or q is Confidence.HIGH): + return True + elif threshold == "medium" and (q is Confidence.CERTAIN or q is Confidence.HIGH or q is Confidence.MEDIUM): + return True + elif threshold == "low" and q is not Confidence.JUNK: + # q is CERTAIN/HIGH/MEDIUM/LOW. + return True + + return False + + +def attempt_object_unpack( + proc: SBProcess, object_pointer: pointer, settings: LLEFSettings, col_settings: LLEFColorSettings +) -> Union[tuple[str, str], None]: + """ + Precondition: must be called with LLEFState.go_state.moduledata_info not None. + Looks up the pointer to see if its type has been deduced. If so, tries to unpack the pointer, and if successful + returns an inline preview of the unpacking. + + :param SBProcess proc: The process object currently being debugged. + :param pointer object_pointer: The raw pointer to fetch a guess for. + :param LLEFSettings settings: The LLEFSettings for retrieving the confidence threshold. + :param LLEFColorSettings col_settings: The LLEFColorSettings for generating the output. + :return Union[tuple[str, str], None]: If extraction succeeded, returns an inline string to be displayed. + Otherwise None. + """ + to_return: Union[tuple[str, str], None] = None + guessed_type_struct: Union[GoType, None] = LLEFState.go_state.type_guesses.search(object_pointer) + if guessed_type_struct is not None: + # Then we made a guess for this pointer as an object. + info = ExtractInfo( + proc=proc, + ptr_size=LLEFState.go_state.pclntab_info.ptr_size, + type_structs=LLEFState.go_state.moduledata_info.type_structs, # type: ignore[union-attr] + ) + obj = guessed_type_struct.extract_at(info, object_pointer, set(), GO_OBJECT_UNPACK_DEPTH) + + if is_sufficient_confidence(obj.confidence(), settings): + type_string = guessed_type_struct.header.name + if len(type_string) > 30: + type_string = type_string[:29] + ".." + + if type_string == "string": + object_string = color_string(obj, col_settings.string_color) + object_string = f'"{object_string}"' + else: + object_string = color_string(obj, col_settings.dereferenced_value_color) + + type_string = color_string(f"({type_string})", col_settings.rebased_address_color) + to_return = (type_string, object_string) + else: + LLEFState.go_state.type_guesses.delete(object_pointer) + + return to_return + + +def attempt_string_decode(buffer: bytes) -> str: + """ + Attempts to decode the buffer with UTF-8. + + :param bytes buffer: The suspected string as bytes. + :return str: The decoded string, otherwise empty string. + """ + string = buffer.decode("utf-8", errors="ignore") + # Respect null termination. + string = string.split("\x00")[0] + return repr(string)[1:-1] + + +def attempt_string_unpack( + proc: SBProcess, str_pointer: pointer, col_settings: LLEFColorSettings, address_containing_pointer=None +) -> Union[str, None]: + """ + Looks up the pointer to see if it's been registered as a possible string pointer. If so, unpacks that string + and attempts to decode it as a Python string. + + :param SBProcess proc: The process object currently being debugged. + :param pointer str_pointer: The raw pointer that could be the base address of a string. + :param LLEFColorSettings col_settings: The LLEFColorSettings object for formatting the output. + :return Union[str, None]: If a length was found for the string and decoding succeeded, + returns inline output. Otherwise None. + """ + to_return: Union[str, None] = None + guessed_string_length: Union[int, None] = LLEFState.go_state.string_guesses.search(str_pointer) + + if guessed_string_length is None and address_containing_pointer is not None: + # Attempt to see if the next value in memory makes sense as a length value. + next_memory_location = address_containing_pointer + LLEFState.go_state.pclntab_info.ptr_size + err = SBError() + try: + size = proc.ReadUnsignedFromMemory(next_memory_location, LLEFState.go_state.pclntab_info.ptr_size, err) + if err.Success() and size > 0: + if str_pointer >= GO_MIN_PTR and size < GO_MIN_PTR: + LLEFState.go_state.string_guesses.add(str_pointer, size) + guessed_string_length = size + except OverflowError: + pass + + if guessed_string_length is not None: + # Then we made a guess for this pointer as a string. + successful_read = False + # Safely attempt to read the memory: + max_pointer_value = 1 << (LLEFState.go_state.pclntab_info.ptr_size * 8) + if str_pointer >= 0 and guessed_string_length > 0 and str_pointer + guessed_string_length <= max_pointer_value: + err = SBError() + buffer = proc.ReadMemory(str_pointer, guessed_string_length, err) + if err.Success() and buffer is not None: + referenced_string = attempt_string_decode(buffer) + if len(referenced_string) > 0: + successful_read = True + to_return = color_string( + referenced_string, + col_settings.string_color, + f' {GLYPHS.RIGHT_ARROW.value} ("', + '")', + ) + + if not successful_read: + LLEFState.go_state.string_guesses.delete(str_pointer) + + return to_return + + +def go_annotate_pointer_line( + proc: SBProcess, + target: SBTarget, + regions: Union[SBMemoryRegionInfoList, None], + pointer_to_annotate: pointer, + address_containing_pointer: Union[pointer, None], + settings: LLEFSettings, + col_settings: LLEFColorSettings, + depth: int = 0, +) -> str: + """ + Go-specific annotations for values in the register or stack context views. + Includes information for code pointers and type structures / corresponding object unpacking attempts. + + :param SBProcess proc: The process object currently being debugged. + :param SBTarget target: The target context, currently being debugged. + :param Union[SBMemoryRegionInfoList, None] regions: If register_coloring, the region list for the process. + :param pointer pointer_to_annotate: The value read on this line of the context view. + :param Union[pointer, None] address_containing_pointer: If on the stack, the address that contains this pointer. + :param LLEFSettings settings: Settings object used for rebasing information. + :param LLEFColorSettings col_settings: Colour settings used for output formatting. + :param int depth: Depth for recursion. + :return str: If annotations could be made, a string containing them. Otherwise empty string. + """ + pointer_value = SBAddress(pointer_to_annotate, target) + line = "" + + rebased = generate_rebased_address_string( + pointer_value, settings.rebase_addresses, settings.rebase_offset, col_settings.rebased_address_color + ) + + if is_code(pointer_to_annotate, proc, target, regions): + name, offset = go_find_func_name_offset(pointer_to_annotate) + if name: + line += f" {rebased} {GLYPHS.RIGHT_ARROW.value} " + line += color_string(f"<{name}+{offset}>", col_settings.dereferenced_value_color) + + else: + if LLEFState.go_state.moduledata_info is not None: + # Attempt type inference. + # Check for the condition that the current pointer is a pointer to a Go type, + # and see if it makes sense that the next sequential pointer is an object of that type. + + # Attempt to see if previous analysis has identified this pointer as a type pointer. + referenced_type_struct = LLEFState.go_state.moduledata_info.type_structs.get(pointer_to_annotate) + if referenced_type_struct is not None: + # Matching type has been found. + next_pointer_unpacked = None + if address_containing_pointer is not None: + # Guess the next pointer in memory aligns with the pointed to type object. + # If the association does not make sense, then the object won't be unpacked anyway. + next_pointer = address_containing_pointer + LLEFState.go_state.pclntab_info.ptr_size + # Associate Go data object with a Go type definition, so the link can be preserved. + # These pointers are stored in a Least-Recently-Used Dictionary structure, so type associations + # will eventually decay. + err = SBError() + object_ptr = proc.ReadUnsignedFromMemory( + next_pointer, + LLEFState.go_state.pclntab_info.ptr_size, + err, + ) + # Sanity check - filter out pointers from integers. + if err.Success() and object_ptr >= GO_MIN_PTR: + # Associate the object pointer with its type for future display. + LLEFState.go_state.type_guesses.add(object_ptr, referenced_type_struct) + unpacked_data = attempt_object_unpack(proc, object_ptr, settings, col_settings) + if unpacked_data: + (_, next_pointer_unpacked) = unpacked_data + + # Markup line with identified Go data type. + go_type_name = color_string(referenced_type_struct.header.name, col_settings.go_type_color) + line += f" {GLYPHS.RIGHT_ARROW.value} Go type ptr: {go_type_name}" + resolved = referenced_type_struct.get_underlying_type(GO_TYPE_DECODE_DEPTH) + if referenced_type_struct.header.name != resolved: + line += " = " + resolved + if next_pointer_unpacked: + line += f", possible match: {next_pointer_unpacked}?" + + else: + # If it's not a type info struct, try unpacking it as an object. + unpacked_data = attempt_object_unpack(proc, pointer_to_annotate, settings, col_settings) + object_at_pointer = None + if unpacked_data: + (resolved_type, object_at_pointer) = unpacked_data + if object_at_pointer: + line += f" {GLYPHS.RIGHT_ARROW.value} {resolved_type} {object_at_pointer}" + + if line == "": + # If it's not any of the above, try as a string. + string_at_pointer = attempt_string_unpack( + proc, pointer_to_annotate, col_settings, address_containing_pointer + ) + if string_at_pointer: + line += string_at_pointer + + if line == "" and depth == 0: + # Check to see if the pointer is to an object we can mark up. + err = SBError() + if pointer_to_annotate > GO_MIN_PTR: + dereferenced_value = proc.ReadPointerFromMemory(pointer_to_annotate, err) + if err.Success(): + recursive_line = go_annotate_pointer_line( + proc, target, regions, dereferenced_value, pointer_to_annotate, settings, col_settings, depth=1 + ) + if recursive_line: + line = f" {GLYPHS.RIGHT_ARROW.value} *{hex(dereferenced_value)}{recursive_line}" + + return line + + +def add_type_guess_to_register_value(frame: SBFrame, reg_name: str, type_struct: GoType) -> None: + """ + Attempt to assign a type guess to the pointer held in the named register. + + :param SBFrame frame: The current frame, to retrieve registers from. + :param str reg_name: The name of the register to inspect. + :param GoType type_struct: The Python type information structure to assign against this register value. + """ + pointer_value = frame.FindRegister(reg_name) + if pointer_value.IsValid(): + pointer = pointer_value.GetValueAsUnsigned() + if pointer >= GO_MIN_PTR: + LLEFState.go_state.type_guesses.add(pointer, type_struct) + + +def go_stop_hook(exe_ctx: SBExecutionContext, arch: BaseArch, settings: LLEFSettings, debugger: SBDebugger) -> None: + """ + This function is called with every refresh of the context view. + Ensures the binary has been statically analysed if not already, and performs dynamic Go analysis. + + :param SBExecutionContext exe_ctx: The execution context for retrieving frame, process and target. + :param BaseArch arch: The class describing our current target architecture. + :param LLEFSettings settings: The LLEFSettings class used to query Go support level. + """ + + frame = exe_ctx.GetFrame() + proc = exe_ctx.GetProcess() + target = exe_ctx.GetTarget() + + # If Go support is disabled, this function will do nothing. + # Only analyse if not done already. + if settings.go_support_level != "disable" and not LLEFState.go_state.analysed: + setup_go(proc, target, settings) + if go_context_analysis(settings): + # The Go runtime issues lots of SIGURG signals which causes a stop by default. + debugger.HandleCommand("process handle SIGURG --stop 0 --notify 0 ") + + # If we have now determined this is not a Go binary / Go support disabled, then quit. + if not go_context_analysis(settings): + return + + arg_registers = get_arg_registers(arch) + (go_min_version, _) = LLEFState.go_state.pclntab_info.version_bounds + if go_min_version >= 17: + # HOOK TASK 1: + # Register-straddling interface/string guessing. Needs register-based calling convention (Go >= 1.17). + for i in range(len(arg_registers) - 1): + # Try type pointer in arg_registers[i], data pointer in arg_registers[i+1] + abi_register = frame.FindRegister(arg_registers[i]) + if abi_register.IsValid(): + arg_value = abi_register.GetValueAsUnsigned() + + found_type = False + if LLEFState.go_state.moduledata_info is not None: + # Attempt to match arg_value against list of known type pointers. + type_struct = LLEFState.go_state.moduledata_info.type_structs.get(arg_value) + if type_struct is not None: + # It's a type information struct, so guess pointer in next register as that type of value. + add_type_guess_to_register_value(frame, arg_registers[i + 1], type_struct) + found_type = True + + if not found_type: + # Attempt to determine if there is a string, where reg i is the pointer and reg i+1 is the length. + next_abi_register = frame.FindRegister(arg_registers[i + 1]) + if next_abi_register.IsValid(): + next_register_value = next_abi_register.GetValueAsUnsigned() + if arg_value >= GO_MIN_PTR and next_register_value < GO_MIN_PTR: + LLEFState.go_state.string_guesses.add(arg_value, next_register_value) + + # HOOK TASK 2: + # Pointer receiver guessing. Needs register-based calling convention and present ModuleData. + pc = frame.GetPC() + record = go_find_func(pc) + if record is not None and LLEFState.go_state.moduledata_info is not None: + (entry, gofunc) = record + if entry != LLEFState.go_state.prev_func: + LLEFState.go_state.prev_func = entry + # Either this function was just called, or we stopped in the middle of it. + # Take one-shot guess at pointer receiver if applicable. + + # Regex is for the literal string .(*X). where X is any string. i.e. a pointer receiver. + match = re.search(r"\.\(\*.*\)\.", gofunc.name) + if match is not None: + ptr_receiver = match.group(0)[3:-2] + path = gofunc.name[: match.start()].split("/") + if len(path) > 0: + module_name = path[-1] + + # Attempt to match the type name against names found in the Go runtime. + type_name = module_name + "." + ptr_receiver + type_obj: Union[GoType, None] = None + for t in LLEFState.go_state.moduledata_info.type_structs.values(): + if t.header.name == type_name: + type_obj = t + break + + if type_obj is not None: + # If the type object was found, lable the pointer receiver register against that type. + # As per https://go.dev/s/regabi, X86 uses RAX/EAX and ARM uses R0, etc. + # (first register in the calling convention) + add_type_guess_to_register_value(frame, arg_registers[0], type_obj) diff --git a/common/golang/constants.py b/common/golang/constants.py new file mode 100644 index 0000000..96bd81f --- /dev/null +++ b/common/golang/constants.py @@ -0,0 +1,67 @@ +"""Go-specific constant definitions.""" + +from collections import namedtuple + +# GO_MAGIC_* enumerates the possibilities for the first 4 bytes of the PCLNTAB. +GO_MAGIC_2_TO_15 = 0xFFFFFFFB +GO_MAGIC_16_TO_17 = 0xFFFFFFFA +GO_MAGIC_18_TO_19 = 0xFFFFFFF0 +GO_MAGIC_20_TO_24 = 0xFFFFFFF1 + +# This list must include all the above magic numbers. +GO_MAGICS = [GO_MAGIC_2_TO_15, GO_MAGIC_16_TO_17, GO_MAGIC_18_TO_19, GO_MAGIC_20_TO_24] + +# Sections in which the PCLNTAB can live. +GO_PCLNTAB_NAMES = [".gopclntab", "__gopclntab"] +# Sections in which the ModuleData structure can live. +GO_NOPTRDATA_NAMES = [".noptrdata", "__noptrdata", ".data"] + +# GO_MD_* enumerates the offsets where useful fields live in the ModuleData section. They are version-specific. +# The offset is an index into the ModuleData structure, cast as an array of pointer-sized ints. + +# Description of fields within ModuleData: +# minpc, maxpc are lower/upper bounds for the program counter - i.e. denotes the text section. +# types, etypes denote the bounds of the types section (storing type information structures) +# typelinks is an array of offsets to these type information structures. The length is typelinks_len. +ModuleDataOffsets = namedtuple("ModuleDataOffsets", ["minpc", "maxpc", "types", "etypes", "typelinks", "typelinks_len"]) +GO_MD_7_ONLY = ModuleDataOffsets(minpc=10, maxpc=11, types=25, etypes=26, typelinks=27, typelinks_len=28) +GO_MD_8_TO_15 = ModuleDataOffsets(minpc=10, maxpc=11, types=25, etypes=26, typelinks=30, typelinks_len=31) +GO_MD_16_TO_17 = ModuleDataOffsets(minpc=20, maxpc=21, types=35, etypes=36, typelinks=40, typelinks_len=41) +GO_MD_18_TO_19 = ModuleDataOffsets(minpc=20, maxpc=21, types=35, etypes=36, typelinks=42, typelinks_len=43) +GO_MD_20_TO_24 = ModuleDataOffsets(minpc=20, maxpc=21, types=37, etypes=38, typelinks=44, typelinks_len=45) + +GO_MAX_SLICE_EXTRACT = 100 # don't extract more than this many elements of a slice +GO_MAX_STRING_READ = 1000 # don't extract more than this many bytes of string + +# Parameters for rate_candidate_length when calculating heuristics. +GO_TUNE_SLICE_THRESHOLD = 1000 +GO_TUNE_SLICE_RATE = 100 +GO_TUNE_STRING_THRESHOLD = 40 +GO_TUNE_STRING_RATE = 5 + +# We'll truncate strings if they're longer than this. +GO_STR_TRUNCATE_LEN = 32 + +# Threshold to separate pointer guesses from numbers by value. +GO_MIN_PTR = 0x1000 + +# Maximum number of directories in swiss map to extract. +GO_MAX_SWISSMAP_DIRS = 65536 + +# Exponent of probability for bitstring entropy, to permit more extraordinary strings. +GO_ENTROPY_SOFTNESS = 0.3 + +GO_DEFAULT_UNPACK_DEPTH = 3 + +# The depth to decode types found and annotated inline (substituting name for type constructors). +GO_TYPE_DECODE_DEPTH = 2 +# The depth to unpack objects found and annotated inline. Strings will always be truncated if too long. +GO_OBJECT_UNPACK_DEPTH = 3 + +# These two control the capacities of least-recently-added dictionaries that store guess information. +# This is a balancing act of: +# 1. Not forgetting types / strings too quickly, possibly even with the same context display. +# 2. Hanging onto types for too long, when the pointer has been garbage collected and is now something else. +# Err on the side of (1), given that a bit of junk being displayed is okay. +GO_TYPE_GUESS_CAPACITY = 64 +GO_STRING_GUESS_CAPACITY = 128 diff --git a/common/golang/data.py b/common/golang/data.py new file mode 100644 index 0000000..b52315d --- /dev/null +++ b/common/golang/data.py @@ -0,0 +1,241 @@ +from dataclasses import dataclass +from enum import Enum + +from common.golang.constants import GO_STR_TRUNCATE_LEN + + +class Confidence(Enum): + JUNK = 1 + LOW = 2 + MEDIUM = 3 + HIGH = 4 + CERTAIN = 5 + + def to_float(self) -> float: + if self is Confidence.JUNK: + return 0.0 + elif self is Confidence.LOW: + return 0.2 + elif self is Confidence.MEDIUM: + return 0.4 + elif self is Confidence.HIGH: + return 0.7 + else: + # CERTAIN (only to provide a 1.0, not for comparing against!) + return 1.0 + + +@dataclass(frozen=True) +class GoData: + """ + Base class for Python representations of Go objects. + """ + + heuristic: float # Internal only: a measure from 0.0-1.0 of how successful the parsing was. + + def confidence(self) -> Confidence: + if self.heuristic < Confidence.LOW.to_float(): + return Confidence.JUNK + elif self.heuristic < Confidence.MEDIUM.to_float(): + return Confidence.LOW + elif self.heuristic < Confidence.HIGH.to_float(): + return Confidence.MEDIUM + else: + return Confidence.HIGH + + +@dataclass(frozen=True) +class GoDataBad(GoData): + """ + The underlying memory region doesn't exist or the values obtained cannot constitute a legal Go object. + """ + + def __str__(self) -> str: + return "?" + + +@dataclass(frozen=True) +class GoDataUnparsed(GoData): + """ + There was more to parse, but we ran out of depth. + The heuristic represents how likely we think the data would be valid. + """ + + address: int + + def __str__(self) -> str: + return hex(self.address) + ".." + + +@dataclass(frozen=True) +class GoDataBool(GoData): + """ + A boolean. + """ + + value: bool + + def __str__(self) -> str: + if self.value: + return "true" + else: + return "false" + + +@dataclass(frozen=True) +class GoDataInteger(GoData): + """ + A uint or int. + """ + + value: int + + def __str__(self) -> str: + return str(self.value) + + +@dataclass(frozen=True) +class GoDataFloat(GoData): + """ + A floating point number. + """ + + value: float + + def __str__(self) -> str: + return str(self.value) + + +@dataclass(frozen=True) +class GoDataComplex(GoData): + """ + A complex number (two floats). + """ + + real: float + imag: float + + def __str__(self) -> str: + return f"({self.real}+{self.imag}i)" + + +@dataclass(frozen=True) +class GoDataArray(GoData): + """ + An array. + """ + + contents: list[GoData] + + def __str__(self) -> str: + build = "[" + for elem in self.contents: + if isinstance(elem, GoDataString): + build += f'"{str(elem)}", ' + else: + build += f"{str(elem)}, " + return build.removesuffix(", ") + "]" + + +@dataclass(frozen=True) +class GoDataSlice(GoData): + """ + A slice. + """ + + base: int + length: int + capacity: int + + # The len(self.contents) may be less than self.length, in the case of a memory read error or truncation. + contents: list[GoData] + + def __str__(self) -> str: + if len(self.contents) == 0: + return f"" + + else: + build = "[" + for elem in self.contents: + build += str(elem) + ", " + build = build.removesuffix(", ") + if len(self.contents) < self.length: + build += f"...{self.length - len(self.contents)} more" + return build + "]" + + +@dataclass(frozen=True) +class GoDataString(GoData): + """ + A string. + """ + + base: int + length: int + + # The len(self.contents) may be less than self.length, in the case of a memory read error. + contents: bytes + + def __str__(self) -> str: + if len(self.contents) == self.length: + full = self.contents.decode("utf-8", "replace") + rep = repr(full) + # Switch single quotes from repr() to double quotes. + if len(rep) >= 2: + rep = rep[1:-1] + if len(rep) > GO_STR_TRUNCATE_LEN: + return rep[: GO_STR_TRUNCATE_LEN - 1] + ".." + else: + return rep + else: + return f"" + + +@dataclass(frozen=True) +class GoDataStruct(GoData): + """ + A struct. + """ + + fields: list[tuple[str, GoData]] + + def __str__(self) -> str: + build = "{" + for f_name, f_val in self.fields: + if isinstance(f_val, GoDataString): + build += f'{f_name}: "{str(f_val)}", ' + else: + build += f"{f_name}: {str(f_val)}, " + build = build.removesuffix(", ") + "}" + return build + + +@dataclass(frozen=True) +class GoDataMap(GoData): + """ + A map. + """ + + entries: list[tuple[GoData, GoData]] + + def __str__(self) -> str: + build = "[" + for key, val in self.entries: + if isinstance(val, GoDataString): + build += f'{key}: "{str(val)}", ' + else: + build += f"{key}: {str(val)}, " + build = build.removesuffix(", ") + "]" + return build + + +@dataclass(frozen=True) +class GoDataPointer(GoData): + """ + A pointer. + """ + + address: int + + def __str__(self) -> str: + return hex(self.address) diff --git a/common/golang/interfaces.py b/common/golang/interfaces.py new file mode 100644 index 0000000..6347cf7 --- /dev/null +++ b/common/golang/interfaces.py @@ -0,0 +1,52 @@ +from dataclasses import dataclass + +from common.constants import pointer +from common.golang.types import GoType + + +@dataclass(frozen=True) +class GoFunc: + """ + A data structure for describing an individual Go function. + """ + + # The name of the function. Can be recovered even if the binary is stripped. + name: str + # The location of the function as an offset into the binary. + file_addr: int + + # A list of pairs of program counter followed by the stack delta at that point in execution - this is the + # difference between the current stack pointer register and the stack pointer as it was at function entry. + # Used to calculate a base frame pointer. + stack_deltas: list[tuple[pointer, int]] + + +@dataclass(frozen=True) +class PCLnTabInfo: + """ + A data structure that stores information retrieved from the PCLNTAB section. + """ + + # The last address in the text section, or the highest possible value the program counter can take. + # Available both as relative to the binary (file) and address at runtime. + max_pc_file: int + max_pc_runtime: pointer + + # A list of pairs of program counter, then GoFunc - the program counter is the entry address of that function. + func_mapping: list[tuple[pointer, GoFunc]] + + # A tuple describing (min_version, max_version). We guarantee that min_version <= actual go version <= max_version. + version_bounds: tuple[int, int] + + # The size of a pointer on this architecture in bytes. + ptr_size: int + + +@dataclass(frozen=True) +class ModuleDataInfo: + """ + A data structure that stores information retrieved from the ModuleData structure. + """ + + # A mapping from type structure address (offset into the binary) to a parsed python GoType struct. + type_structs: dict[int, GoType] diff --git a/common/golang/moduledata_parser.py b/common/golang/moduledata_parser.py new file mode 100644 index 0000000..8e22861 --- /dev/null +++ b/common/golang/moduledata_parser.py @@ -0,0 +1,222 @@ +"""Class for extracting information from a Go moduledata structure.""" + +import struct +from typing import Union + +from lldb import UINT32_MAX, SBData, SBError, SBProcess, SBTarget + +from common.constants import pointer +from common.golang.constants import GO_MD_7_ONLY, GO_MD_8_TO_15, GO_MD_16_TO_17, GO_MD_18_TO_19, GO_MD_20_TO_24 +from common.golang.interfaces import ModuleDataInfo +from common.golang.types import GoType, PopulateInfo, TypeHeader +from common.golang.util_stateless import file_to_load_address, read_varint +from common.state import LLEFState + + +class ModuleDataParser: + """ + Stores information about the ModuleData context and parses type information from it. + + Latest ModuleData struct information found at: https://github.com/golang/go/blob/master/src/runtime/symtab.go. + """ + + section_offset: int + + # Start of the 'types' section pointed to by the Go moduledata struct. Name aligns with Go source. + types: pointer + + # End of the 'types' section pointed to by the Go moduledata struct. Name aligns with Go source. + etypes: pointer + + # typelinks is an array of offsets to these type information structures. The length is typelinks_len. + # Name aligns with Go source. + typelinks: int + typelinks_len: int + + # A map holding successfully parsed GoType Python structures with their associated addresses. + # Name aligns with Go source. + __type_structs: dict[pointer, GoType] + + def __init__(self, section_offset: int) -> None: + self.section_offset = section_offset + self.__type_structs = {} + + def get_name(self, type_section: bytes, name_offset: int, header: TypeHeader) -> Union[str, None]: + """ + Run the version-specific procedure for decoding the name of a type from memory. + + :param bytes type_section: Slice of program memory from self.types to self.etypes. + :param int name_offset: The offset within the section to begin reading at. + :param TypeHeader header: Information about this type, such as the tflag. + :return Union[str, None]: If name_offset is valid, returns the decoded name. Otherwise, None. + """ + + name = None + # Check that pointer + offset doesn't exceed the end pointer for the types section. + if self.types + name_offset < self.etypes: + # Module data layout depends on the Go version. + (go_min_version, go_max_version) = LLEFState.go_state.pclntab_info.version_bounds + if go_min_version >= 17: + length, name_offset = read_varint(type_section, name_offset) + if self.types + name_offset + length <= self.etypes: + name = type_section[name_offset : name_offset + length].decode("utf-8", "replace") + # Sometimes names start with an extraneous asterisk (*) - tflag tells us when. + if header.tflag & 2: + name = name[1:] + + elif go_max_version <= 16: + (length,) = struct.unpack_from(">H", type_section, name_offset) + name_offset += 2 + if self.types + name_offset + length <= self.etypes: + name = type_section[name_offset : name_offset + length].decode("utf-8", "replace") + if header.tflag & 2: + name = name[1:] + + return name + + def parse_type(self, type_section: bytes, type_offset: int) -> bool: + """ + Decodes and adds to internal state an individual type information structure. + + :param bytes type_section: Slice of program memory from self.types to self.etypes. + :param int offset: The offset within the section to begin parsing at. + :return bool: If parsing the type (and all children/parent types) succeeds, returns True. Otherwise False. + """ + type_address = self.types + type_offset + if type_address in self.__type_structs: + # Type already parsed. + return True + + ptr_size = LLEFState.go_state.pclntab_info.ptr_size + if ptr_size == 4: + ptr_specifier = "I" + else: + # ptr_size == 8 here. + ptr_specifier = "Q" + + # Send some useful information to populate(). + info = PopulateInfo(types=self.types, etypes=self.etypes, ptr_size=ptr_size, ptr_specifier=ptr_specifier) + + type_entry_width = ptr_size * 4 + 16 + # Check that struct.unpack_from() won't read outside bounds. + if type_address + type_entry_width <= self.etypes: + header = TypeHeader() + + # Luckily, this format has remained the same for all Go versions since inception. + unpacker = "<" + ptr_specifier * 2 + "IBBBB" + ptr_specifier * 2 + "II" + tup = struct.unpack_from(unpacker, type_section, type_offset) + ( + header.size, # usize + header.ptrbytes, # usize + header.t_hash, # uint32 + header.tflag, # uint8 + header.align, # uint8 + header.fieldalign, # uint8 + header.kind, # uint8 + _, # (equal) usize + _, # (gcdata) usize + name_offset, # uint32 + ptr_to_this_offset, # uint32 + ) = tup + + name_offset += 1 + name = self.get_name(type_section, name_offset, header) + if name is not None: + header.name = name + + go_type = GoType.make_from(header, LLEFState.go_state.pclntab_info.version_bounds) + + if go_type is not None: + # Each type has a corresponding populate() function. + type_struct_pointers = go_type.populate(type_section, type_offset + type_entry_width, info) + + # If an error occurred during parsing: type_struct_pointers is None + # Otherwise, if simple data type: type_struct_pointers is [] + # Otherwise, if complex data type: it's a list of pointers go walk over next (recursively). + if type_struct_pointers is not None: + self.__type_structs[type_address] = go_type + + processing_valid = True + for type_addr in type_struct_pointers: + if self.types <= type_addr < self.etypes: + if not self.parse_type(type_section, type_addr - self.types): + processing_valid = False + break + else: + processing_valid = False + break + + if processing_valid and ptr_to_this_offset not in (0, UINT32_MAX): + if not self.parse_type(type_section, ptr_to_this_offset): + processing_valid = False + return processing_valid + + return False + + def parse(self, proc: SBProcess, data: SBData, target: SBTarget) -> Union[ModuleDataInfo, None]: + """ + Attempts to parse a candidate ModuleData, as located by self.section_offset. + + :param SBProcess proc: The process currently being debugged. + :param SBData data: The buffer holding the candidate ModuleData structure. + :param SBTarget target: The target associated with the process. Used for resolving file->load addresses. + :return Union[ModuleDataInfo, None]: If run on a real ModuleData, and a supported Go version, then returns + the parsed information as a data structure. Otherwise None. + """ + + offsets = None + + (min_go, max_go) = LLEFState.go_state.pclntab_info.version_bounds + if min_go == 7 and max_go == 7: + offsets = GO_MD_7_ONLY + if min_go >= 8 and max_go <= 15: + offsets = GO_MD_8_TO_15 + elif min_go >= 16 and max_go <= 17: + offsets = GO_MD_16_TO_17 + elif min_go >= 18 and max_go <= 19: + offsets = GO_MD_18_TO_19 + elif min_go >= 20 and max_go <= 24: + offsets = GO_MD_20_TO_24 + + module_data_info = None + + if offsets is not None: + if LLEFState.go_state.pclntab_info.ptr_size == 4: + reader = data.uint32[self.section_offset // 4 :] + else: + # ptr_size == 8 here. + reader = data.uint64[self.section_offset // 8 :] + + # Use these fields as a sanity check, to ensure we really did find ModuleData. + min_program_counter = reader[offsets.minpc] + max_program_counter = reader[offsets.maxpc] + first_function_address = LLEFState.go_state.pclntab_info.func_mapping[0][1].file_addr + if ( + min_program_counter == first_function_address + and max_program_counter == LLEFState.go_state.pclntab_info.max_pc_file + ): + self.types = file_to_load_address(target, reader[offsets.types]) + # -1 +1 so that we don't miss the end of the section by 1, and the file->load resolution then fails. + self.etypes = file_to_load_address(target, reader[offsets.etypes] - 1) + 1 + self.typelinks = file_to_load_address(target, reader[offsets.typelinks]) + self.typelinks_len = reader[offsets.typelinks_len] + + err = SBError() + type_section = proc.ReadMemory(self.types, self.etypes - self.types, err) + if err.Success() and type_section is not None: + read_success = True + for i in range(self.typelinks_len): + err = SBError() + offset = proc.ReadUnsignedFromMemory(self.typelinks + i * 4, 4, err) + if err.Fail(): + read_success = False + if not self.parse_type(type_section, offset): + read_success = False + + # Now we have discovered everything, go and fill in links from type to type. + if read_success and len(self.__type_structs) > 0: + for go_type in self.__type_structs.values(): + go_type.fixup_types(self.__type_structs) + module_data_info = ModuleDataInfo(type_structs=self.__type_structs) + + return module_data_info diff --git a/common/golang/pclntab_parser.py b/common/golang/pclntab_parser.py new file mode 100644 index 0000000..ea668e5 --- /dev/null +++ b/common/golang/pclntab_parser.py @@ -0,0 +1,371 @@ +"""Class for extracting information from a Go pclntab section.""" + +import itertools +from collections.abc import Callable, Iterator +from typing import Any, Union + +from lldb import SBData, SBError, SBProcess, SBTarget + +from common.constants import pointer +from common.golang.constants import GO_MAGIC_2_TO_15, GO_MAGIC_16_TO_17, GO_MAGIC_18_TO_19, GO_MAGIC_20_TO_24 +from common.golang.interfaces import GoFunc, PCLnTabInfo +from common.golang.util_stateless import file_to_load_address, read_varint + + +class PCLnTabParser: + """ + Stores information retrieved from the pclntab header and parses further data. + """ + + # Extracted from the binary: + base: int + magic: int + pad: int + min_instr_size: int + ptr_size: int + num_funcs: int + num_files: int # unset in Go < 1.16 + text_start: int # unset in Go < 1.18 + func_name_off: int # unset in Go < 1.16 + cu_off: int # unset in Go < 1.16 + file_off: int # unset in Go < 1.16 + pc_off: int # unset in Go < 1.16 + pc_ln_off: int + + # Helper properties: + + valid: bool + + __make_func_map: Callable[[SBProcess, SBTarget, SBData], bool] + """ + Parses the map from function entry addresses to human-readable names. + + :param SBProcess proc: The process object currently being debugged. + :param SBData buf: The data buffer holding the section associated with gopclntab. + :return int: Returns True if the parsing succeeded. + """ + + __read_header: Callable[[list[int]], None] + """ + Set up internal state from the PCLNTAB header. + + :param list[int] reader: List of pointer-sized integers read after the first 8 bytes of the PCLNTAB. + """ + + __get_int_reader: Callable[[SBData], Any] + """ + Return an appropriate reader for ints of size self.ptr_size bytes. + + :return Any: An array-like object allowing indexed access to the integers. + """ + + __unnamed_ctr: Iterator[int] + + # State to be returned after parsing: + max_pc_file: int + func_mapping: list[tuple[pointer, GoFunc]] + version_bounds: tuple[int, int] + + def __init__(self, pclntab_base: int, magic: int, pad: int, min_instr_size: int, ptr_size: int) -> None: + """Initialise the PCLnTabParser, set up internal state ready for parsing. + + Args: + pclntab_base (int): Offset into the binary file of the PcLnTab section. + magic (int): Go-version specific magic bytes for PcLnTab section. + pad (int): Number of padding bytes (found in the PcLnTab header). + min_instr_size (int): Bytes for minimum instruction size. + ptr_size (int): Size of pointers in bytes. + """ + + # Assumes a little-endian format, since LLEF only supports LE architectures for now. + self.base = pclntab_base + self.magic = magic + self.pad = pad + self.min_instr_size = min_instr_size + self.ptr_size = ptr_size + + self.__unnamed_ctr = itertools.count() + + # Check that values provided in PcLnTab header are sensible. + self.valid = self.pad == 0 and self.min_instr_size in (1, 2, 4) and self.ptr_size in (4, 8) + self.func_mapping = [] + + # Go ints are the same width as pointers + if self.ptr_size == 4: + self.__get_int_reader = lambda buf: buf.uint32 + else: + self.__get_int_reader = lambda buf: buf.uint64 + + if self.magic == GO_MAGIC_20_TO_24: + self.__read_header = self.read_header_18to24 + self.__make_func_map = self.make_func_map_18to24 + self.version_bounds = (20, 24) + elif self.magic == GO_MAGIC_18_TO_19: + self.__read_header = self.read_header_18to24 + self.__make_func_map = self.make_func_map_18to24 + self.version_bounds = (18, 19) + elif self.magic == GO_MAGIC_16_TO_17: + self.__read_header = self.read_header_16to17 + self.__make_func_map = self.make_func_map_16to17 + self.version_bounds = (16, 17) + elif self.magic == GO_MAGIC_2_TO_15: + self.__read_header = self.read_header_2to15 + self.__make_func_map = self.make_func_map_2to15 + self.version_bounds = (2, 15) + else: + self.valid = False + + def make_stackmap(self, target: SBTarget, bytebuf: Any, offset: int, entry: int) -> list[tuple[int, int]]: + """Parses the zig-zag, varint-encoded data relating PC offsets and SP deltas for a given function. + + :param SBTarget target: The target associated with the process. Used for resolving file->load addresses. + :param Any bytebuf: An array-like object allowing indexed access to bytes. + :param int offset: The address offset into the byte buffer to start reading from. + :param int entry: The first address containing code for the target function. + :return list[tuple[int, int]]: A list of pairs of program counter followed by stack pointer delta. + """ + pc = file_to_load_address(target, entry) + spdelta = -1 + pairs = [] + while True: + vdelta, offset = read_varint(bytebuf, offset) + if vdelta == 0 and pc > entry: + break + + # vdelta is zig-zag encoded. + if vdelta & 1 != 0: + vdelta = -((vdelta + 1) >> 1) + else: + vdelta >>= 1 + + pcdelta, offset = read_varint(bytebuf, offset) + spdelta += vdelta + pairs.append((pc, spdelta)) + pc += pcdelta * self.min_instr_size + + return pairs + + def add_func( + self, proc: SBProcess, target: SBTarget, static_entry: int, nameptr: int, stack_deltas: list[tuple[int, int]] + ) -> None: + """ + Add a single GoFunc entry to state by reading a string from memory. + + :param SBProcess proc: The process object currently being debugged. + :param SBTarget target: The target associated with the process. Used for resolving file->load addresses. + :param int static_entry: The file address for the entrypoint of the function we wish to add. + :param int nameptr: A pointer to the string in memory, to use as the function name. + :param list[tuple[int, int]] stack_deltas: The list of stack deltas to pass to the GoFunc. + """ + err = SBError() + name = proc.ReadCStringFromMemory(nameptr, 256, err) + if err.Fail(): + name = f"Unnamed_{next(self.__unnamed_ctr)}" + + record = GoFunc(name=name, file_addr=static_entry, stack_deltas=stack_deltas) + + runtime_entry = file_to_load_address(target, static_entry) + self.func_mapping.append((runtime_entry, record)) + + def make_func_map_18to24(self, proc: SBProcess, target: SBTarget, buf: SBData) -> bool: + """ + Parses the map from function entry addresses to GoFunc structures for 1.18 <= Go <= 1.24. + + :param SBProcess proc: The process object currently being debugged. + :param SBTarget target: The target associated with the process. Used for resolving file->load addresses. + :param SBData buf: The data buffer holding the section associated with gopclntab. + :return bool: Returns True if the parsing succeeded. + """ + funcname_tab_addr = self.base + self.func_name_off + + # set up to read uint32s + start = self.pc_ln_off // 4 + if start * 4 != self.pc_ln_off: + # we must be 4-byte aligned + return False + + pairs: list[int] = buf.uint32[start : start + 1 + self.num_funcs * 2] + for func_i in range(self.num_funcs): + func_entry = self.text_start + pairs[2 * func_i] + funcinfo = (self.pc_ln_off + pairs[2 * func_i + 1] + 4) // 4 + [name_offset, _, _, pcsp] = buf.uint32[funcinfo : funcinfo + 4] + stackmap = self.make_stackmap(target, buf.uint8, self.pc_off + pcsp, func_entry) + self.add_func(proc, target, func_entry, funcname_tab_addr + name_offset, stackmap) + + self.max_pc_file = self.text_start + pairs[2 * self.num_funcs] + return True + + def make_func_map_16to17(self, proc: SBProcess, target: SBTarget, buf: SBData) -> bool: + """ + Parses the map from function entry addresses to GoFunc structures for 1.16 <= Go < 1.18. + + :param SBProcess proc: The process object currently being debugged. + :param SBTarget target: The target associated with the process. Used for resolving file->load addresses. + :param SBData buf: The data buffer holding the section associated with gopclntab. + :return bool: Returns True if the parsing succeeded. + """ + funcname_tab_addr = self.base + self.func_name_off + + # entries are pairs of pointer-sized numbers. + start = self.pc_ln_off // self.ptr_size + if start * self.ptr_size != self.pc_ln_off: + # we must be aligned + return False + + pairs: list[int] = self.__get_int_reader(buf)[start : start + 1 + self.num_funcs * 2] + for func_i in range(self.num_funcs): + func_entry = pairs[2 * func_i] + funcinfo = (self.pc_ln_off + pairs[2 * func_i + 1] + self.ptr_size) // 4 + [name_offset, _, _, pcsp] = buf.uint32[funcinfo : funcinfo + 4] + stackmap = self.make_stackmap(target, buf.uint8, self.pc_off + pcsp, func_entry) + self.add_func(proc, target, func_entry, funcname_tab_addr + name_offset, stackmap) + + self.max_pc_file = pairs[2 * self.num_funcs] + return True + + def make_func_map_2to15(self, proc: SBProcess, target: SBTarget, buf: SBData) -> bool: + """ + Parses the map from function entry addresses to GoFunc structures for 1.2 <= Go < 1.16. + + :param SBProcess proc: The process object currently being debugged. + :param SBTarget target: The target associated with the process. Used for resolving file->load addresses. + :param SBData buf: The data buffer holding the section associated with gopclntab. + :return bool: Returns True if the parsing succeeded. + """ + + # entries are pairs of pointer-sized numbers. + start = self.pc_ln_off // self.ptr_size + if start * self.ptr_size != self.pc_ln_off: + # we must be aligned + return False + + pairs: list[int] = self.__get_int_reader(buf)[start : start + 1 + self.num_funcs * 2] + for func_i in range(self.num_funcs): + func_entry = pairs[2 * func_i] + funcinfo = (pairs[2 * func_i + 1] + self.ptr_size) // 4 + [name_offset, _, _, pcsp] = buf.uint32[funcinfo : funcinfo + 4] + stackmap = self.make_stackmap(target, buf.uint8, pcsp, func_entry) + self.add_func(proc, target, func_entry, self.base + name_offset, stackmap) + + self.max_pc_file = pairs[2 * self.num_funcs] + return True + + def read_header_18to24(self, reader: list[int]) -> None: + """ + Set up internal state from the PCLNTAB header for 1.18 <= Go <= 1.24. + + :param list[int] reader: List of pointer-sized integers read after the first 8 bytes of the PCLNTAB. + """ + [ + self.num_funcs, + self.num_files, + self.text_start, + self.func_name_off, + self.cu_off, + self.file_off, + self.pc_off, + self.pc_ln_off, + *_, + ] = reader + + def read_header_16to17(self, reader: list[int]) -> None: + """ + Set up internal state from the PCLNTAB header for 1.16 <= Go < 1.24. + + :param list[int] reader: List of pointer-sized integers read after the first 8 bytes of the PCLNTAB. + """ + [ + self.num_funcs, + self.num_files, + self.func_name_off, + self.cu_off, + self.file_off, + self.pc_off, + self.pc_ln_off, + *_, + ] = reader + + def read_header_2to15(self, reader: list[int]) -> None: + """ + Set up internal state from the PCLNTAB header for 1.2 <= Go < 1.16. + + :param list[int] reader: List of pointer-sized integers read after the first 8 bytes of the PCLNTAB. + """ + self.num_funcs = reader[0] + self.pc_ln_off = 8 + self.ptr_size + + def differentiate_versions(self) -> tuple[int, int]: + """ + Uses the list of function names from the PCLNTAB to more finely differentiate between Go versions. + This analysis is used in ModuleData parsing, where the internal layout changes more frequently. + + :return tuple[int, int]: Tighter version bounds. + """ + func_names = list(map(lambda x: x[1].name, self.func_mapping)) + new_bounds = self.version_bounds + # We search for internal functions that are always included (architecture-independent). i.e. core runtime funcs. + + if self.version_bounds == (20, 24): + # The 1.24 update removed runtime.evacuate as part of the old map implementation by default. + # However, a user could still compile with the old map implementation turned on. So we don't have an "else". + if "runtime.evacuate" not in func_names: + new_bounds = (24, 24) + + elif self.version_bounds == (18, 19): + # The 1.19 update renamed runtime.findrunnable to runtime.findRunnable. + if "runtime.findrunnable" in func_names: + new_bounds = (18, 18) + elif "runtime.findRunnable" in func_names: + new_bounds = (19, 19) + + elif self.version_bounds == (16, 17): + # The 1.17 update renamed runtime.freespecial to runtime.freeSpecial. + if "runtime.freespecial" in func_names: + new_bounds = (16, 16) + elif "runtime.freeSpecial" in func_names: + new_bounds = (17, 17) + + elif self.version_bounds == (2, 15): + # The 1.8 update added the function runtime.modulesinit. + if "runtime.modulesinit" in func_names: + # We are 1.8-1.15 inclusive. + # The 1.9 update removed the function runtime.lfstackpush (replacing with runtime.(*lfstack).push) + if "runtime.lfstackpush" in func_names: + new_bounds = (8, 8) + else: + new_bounds = (9, 15) + + else: + # We are 1.2-1.7 inclusive. + # The 1.7 update added the function runtime.typelinksinit. + if "runtime.typelinksinit" in func_names: + new_bounds = (7, 7) + else: + new_bounds = (2, 6) + return new_bounds + + def parse(self, proc: SBProcess, target: SBTarget, buf: SBData) -> Union[PCLnTabInfo, None]: + """ + Run parsing on the PCLNTAB for a Go binary. Uses the correctly-selected version-specific subroutines. + + :param SBProcess proc: The process object currently being debugged. + :param SBTarget target: The target associated with the process. Used for resolving file->load addresses. + :param SBData buf: The data buffer holding the section associated with gopclntab. + :return Union[PCLnTabInfo, None]: If parsing succeeds: returns a completed PCLnTabInfo. Otherwise None. + """ + + if self.valid: + reader = self.__get_int_reader(buf)[8 // self.ptr_size :] + + self.__read_header(reader) + + if self.__make_func_map(proc, target, buf): + self.version_bounds = self.differentiate_versions() + return PCLnTabInfo( + max_pc_file=self.max_pc_file, + max_pc_runtime=file_to_load_address(target, self.max_pc_file), + func_mapping=self.func_mapping, + version_bounds=self.version_bounds, + ptr_size=self.ptr_size, + ) + return None diff --git a/common/golang/state.py b/common/golang/state.py new file mode 100644 index 0000000..1450691 --- /dev/null +++ b/common/golang/state.py @@ -0,0 +1,35 @@ +"""Go state module.""" + +from typing import Union + +from common.golang.constants import GO_STRING_GUESS_CAPACITY, GO_TYPE_GUESS_CAPACITY +from common.golang.interfaces import ModuleDataInfo, PCLnTabInfo +from common.golang.util_stateless import LeastRecentlyAddedDictionary + + +class GoState: + """ + State class, encapsulated by global LLEF state - stores Go-specific analysis. + """ + + is_go_binary: bool # set once, based on static analysis + + analysed: bool # ensures we only run the analysis once + + moduledata_info: Union[ModuleDataInfo, None] # moduledata_info might be None, e.g. legacy Go version + pclntab_info: PCLnTabInfo # if is_go_binary is True, then pclntab_info is always valid + + # maps a raw pointer to its guessed datatype + type_guesses: LeastRecentlyAddedDictionary # dict[int, GoType] + # maps a string base address to its guessed length + string_guesses: LeastRecentlyAddedDictionary # dict[int, int] + + prev_func: int # the entry address of the function executing in the previous stop + + def __init__(self) -> None: + self.is_go_binary = False + self.analysed = False + self.moduledata_info = None + self.type_guesses = LeastRecentlyAddedDictionary(capacity=GO_TYPE_GUESS_CAPACITY) + self.string_guesses = LeastRecentlyAddedDictionary(capacity=GO_STRING_GUESS_CAPACITY) + self.prev_func = 0 diff --git a/common/golang/static.py b/common/golang/static.py new file mode 100644 index 0000000..95a5308 --- /dev/null +++ b/common/golang/static.py @@ -0,0 +1,234 @@ +"""Functions that do (short) one-shot static analysis of a loaded Go binary.""" + +import struct +from dataclasses import dataclass +from typing import Iterator, Union + +from lldb import SBData, SBError, SBModule, SBProcess, SBTarget, eByteOrderLittle + +from common.constants import MSG_TYPE, pointer +from common.golang.constants import GO_MAGICS, GO_NOPTRDATA_NAMES, GO_PCLNTAB_NAMES +from common.golang.interfaces import ModuleDataInfo +from common.golang.moduledata_parser import ModuleDataParser +from common.golang.pclntab_parser import PCLnTabParser +from common.output_util import print_message +from common.settings import LLEFSettings +from common.state import LLEFState + + +def parse_pclntab(proc: SBProcess, target: SBTarget, buf: SBData, file_addr: int) -> bool: + """ + Attempts to parse the PCLNTAB from a Go binary. + + :param SBProcess proc: The process object associated with the target. + :param SBTarget target: The target program running under the debugger, for resolving file->load addresses. + :param SBData buf: Binary buffer containing gopclntab. + :param int file_addr: The file address at which the PCLNTAB's magic bytes are found. + :return bool: Returns True if parsing succeeded (high confidence of actual Go binary). + """ + + err = SBError() + first8bytes = buf.ReadRawData(err, 0, 8) + if err.Success() and first8bytes is not None: + (magic, pad, min_instr_size, ptr_size) = struct.unpack(" Union[ModuleDataInfo, None]: + """ + Attempts to parse the ModuleData structure from a Go binary. This analysis must not be run before parse_pclntab. + + :param SBProcess proc: The process object associated with the target. + :param SBModule module: The debugger module associated with the target. Used for finding sections. + :param SBTarget target: The target program running under the debugger, for resolving file->load addresses. + :param int pclntab_base: The address of the PCLNTAB, to aid searching for ModuleData. + + :return Union[ModuleDataInfo, None]: If parsing was successful, returns a completed ModuleDataInfo. Else None. + """ + + # The search value (pclntab_address) is the address of the PCLNTAB section encoded at the same + # width as a pointer, and assumed Little-Endian since LLEF only currently supports LE. + if LLEFState.go_state.pclntab_info.ptr_size == 4: + pclntab_address = struct.pack(" Iterator[CandidatePCLnTab]: + """ + An iterator through possible PCLNTAB locations. Tries specific section names at first and falls back to a byte scan + for the magic value. + This is an iterator, rather than returning a list, because the suggestions are ordered in progressive order of + computational intensity. Iterators are lazy so we don't have to do the expensive ones if a cheap one succeeds. + + :param SBModule module: The process object associated with the target. + :param SBTarget target: The target program running under the debugger. + :return Iterator[Candidate]: An iterator over results, each consisting of the containing buffer and location info. + """ + + # ELF and Mach-O formats + for pclntab_name in GO_PCLNTAB_NAMES: + section = module.FindSection(pclntab_name) + if section is not None and section.IsValid(): + section_data = section.GetSectionData() + if section_data is not None and section_data.IsValid(): + yield CandidatePCLnTab( + buffer=section_data, + file_address=section.GetFileAddress(), + load_address=section.GetLoadAddress(target), + ) + + # Check if Windows + windows = False + header = module.GetSectionAtIndex(0) + if header is not None and header.IsValid(): + header_data = header.GetSectionData() + if header_data is not None and header_data.IsValid(): + first_two_bytes = header_data.uint8[0:2] + # 'MZ' or 'ZM' magic number. + if first_two_bytes in ([0x4D, 0x5A], [0x5A, 0x4D]): + windows = True + + read_only_data = None + rdata_sect = None + if windows: + # *********************************************************************************** + # Upon reaching this point, we're about to do some heavy static scanning of the binary. + # This is okay if the user has explicitly forced Go mode, but otherwise (auto) we should + # quit and wait for the user to do that later on. + # *********************************************************************************** + if settings.go_support_level == "auto": + settings.set("go_support_level", "disable") + LLEFState.go_state.analysed = False + return + + # Heavy scanning permitted from here. + # Obtain read-only data as Python bytes + rdata_sect = module.FindSection(".rdata") + if rdata_sect is not None and rdata_sect.IsValid(): + rdata = rdata_sect.GetSectionData() + if rdata is not None and rdata.IsValid(): + err = SBError() + rdata_bytes = rdata.ReadRawData(err, 0, rdata.GetByteSize()) + if err.Success() and rdata_bytes is not None: + read_only_data = rdata_bytes + + # read_only_data not None implies rdata_sect not None, but the type checker doesn't know this. + if read_only_data is not None and rdata_sect is not None: + # If successful, initiate a manual search for PCLNTAB over each value it could start with. + print_message(MSG_TYPE.INFO, "PE binary detected. Scanning for Golang...") + ptr_size = module.GetAddressByteSize() + # struct.iter_unpack requires that read_only_data be a multiple of 4 bytes. We just ensure our local copy is + # a multiple of the pointer size (which will be 4 or 8) for easier alignment. + while len(read_only_data) % ptr_size != 0: + read_only_data += b"\x00" + + for magic in GO_MAGICS: + search_pattern = struct.pack(" None: + """ + Called once for a newly-loaded binary. Sets up go_state. + settings.go_support_level is either auto or force, and go_state.analysed is False. + + :param SBProcess proc: The process object associated with the target. + :param SBTarget target: The target program running under the debugger. + """ + LLEFState.go_state.analysed = True + + # The executable has always been observed at module 0. + module = target.GetModuleAtIndex(0) + + if module.IsValid(): + for candidate in pclntab_candidates(module, target, settings): + LLEFState.go_state.is_go_binary = parse_pclntab(proc, target, candidate.buffer, candidate.load_address) + if LLEFState.go_state.is_go_binary: + print_message(MSG_TYPE.SUCCESS, "Golang detected. Parsing type information...") + LLEFState.go_state.moduledata_info = parse_moduledata(proc, module, target, candidate.file_address) + if LLEFState.go_state.moduledata_info is not None: + print_message(MSG_TYPE.SUCCESS, "Type information found.") + + else: + print_message(MSG_TYPE.ERROR, "No type information available.") + break diff --git a/common/golang/type_getter.py b/common/golang/type_getter.py new file mode 100644 index 0000000..571fae7 --- /dev/null +++ b/common/golang/type_getter.py @@ -0,0 +1,333 @@ +"""A utility class that parses type names into corresponding structures.""" + +import math +from dataclasses import dataclass +from typing import Union + +from common.golang.types import ( + GoType, + GoTypeArray, + GoTypeBool, + GoTypeComplex64, + GoTypeComplex128, + GoTypeFloat32, + GoTypeFloat64, + GoTypeInt, + GoTypeInt8, + GoTypeInt16, + GoTypeInt32, + GoTypeInt64, + GoTypeMap, + GoTypePointer, + GoTypeSlice, + GoTypeString, + GoTypeStruct, + GoTypeStructField, + GoTypeUint, + GoTypeUint8, + GoTypeUint16, + GoTypeUint32, + GoTypeUint64, + GoTypeUintptr, + GoTypeUnsafePointer, + TypeHeader, +) +from common.state import LLEFState + + +@dataclass(frozen=True) +class SimpleType: + """ + SimpleType represents information about a unit type, such as int/bool/unsafe.Pointer. + """ + + go_type: type[GoType] + size: int + alignment: int + + +class TypeGetter: + """ + TypeGetter is a parser that turns type names into the corresponding structures. It's used when a user-provided type + does not exactly match any of those present in the runtime, so we attempt to construct it from scratch. + """ + + __version: tuple[int, int] + __ptr_size: int + __name_to_type: dict[str, GoType] + + __simple_map: dict[str, SimpleType] + + def __slice_to_type(self, slice_repr: str) -> Union[GoTypeSlice, None]: + """ + Parses the string representation of a Go slice type. The string must start with "[]". + + :param slice_repr str: The string representation of a Go slice type. + :return Union[GoTypeSlice, None]: Returns a GoTypeSlice if the provided string is valid, otherwise None. + """ + resolved: Union[GoTypeSlice, None] = None + elem_type = self.string_to_type(slice_repr[2:]) + if elem_type is not None: + header = TypeHeader() + header.align = self.__ptr_size + + # Slices store three ints: base address, length, capacity. + header.size = 3 * self.__ptr_size + resolved = GoTypeSlice(header=header, version=self.__version) + resolved.child_type = elem_type + + return resolved + + def __array_to_type(self, array_repr: str) -> Union[GoTypeArray, None]: + """ + Parses the string representation of a Go array type. The string must start with "[N]", where N is a number. + + :param array_repr str: The string representation of a Go array type. + :return Union[GoTypeArray, None]: Returns a GoTypeArray if the provided string is valid, otherwise None. + """ + resolved: Union[GoTypeArray, None] = None + partitioned = array_repr[1:].split("]", maxsplit=1) + if len(partitioned) == 2: + [length_string, elem_string] = partitioned + + valid_length = True + length = 0 + try: + length = int(length_string, base=0) + except ValueError: + valid_length = False + + if valid_length: + elem_type = self.string_to_type(elem_string) + if elem_type is not None: + header = TypeHeader() + header.align = elem_type.header.align + rounded_size = ((elem_type.header.size + header.align - 1) // header.align) * header.align + header.size = length * rounded_size + resolved = GoTypeArray(header=header, version=self.__version) + resolved.length = length + resolved.child_type = elem_type + return resolved + + def __pointer_to_type(self, pointer_repr: str) -> Union[GoTypePointer, None]: + """ + Parses the string representation of a Go pointer type. The string must start with "*". + + :param pointer_repr str: The string representation of a Go pointer type. + :return Union[GoTypePointer, None]: Returns a GoTypePointer if the provided string is valid, otherwise None. + """ + resolved: Union[GoTypePointer, None] = None + deref_type = self.string_to_type(pointer_repr[1:]) + if deref_type is not None: + header = TypeHeader() + header.align = self.__ptr_size + header.size = self.__ptr_size + resolved = GoTypePointer(header=header, version=self.__version) + resolved.child_type = deref_type + return resolved + + def __struct_to_type(self, struct_repr: str) -> Union[GoTypeStruct, None]: + """ + Parses the string representation of a Go struct type. The string must start with "struct". + + :param struct_repr str: The string representation of a Go struct type. + :return Union[GoTypeStruct, None]: Returns a GoTypeStruct if the provided string is valid, otherwise None. + """ + resolved: Union[GoTypeStruct, None] = None + body = struct_repr[6:].strip() + if body.startswith("{") and body.endswith("}"): + body = body[1:-1].strip() + + valid = True + field_list: list[GoTypeStructField] = [] + + # Track level of {} nestedness. + level = 0 + field_string = "" + fields = [] + for char in body: + if level == 0 and char == ";": + fields.append(field_string) + field_string = "" + else: + field_string += char + if char == "{": + level += 1 + elif char == "}": + level -= 1 + if len(field_string) > 0: + fields.append(field_string) + + offset = 0 + alignment = 1 + for field in fields: + partitioned = field.strip().split(" ", maxsplit=1) + if len(partitioned) == 2: + [name, field_string] = partitioned + field_type = self.string_to_type(field_string) + if field_type is not None: + alignment = math.lcm(alignment, field_type.header.align) + # pad until the field can live in the struct + while offset % field_type.header.align != 0: + offset += 1 + + struct_field = GoTypeStructField(offset=offset, name=name, type_addr=0) + offset += field_type.header.size + struct_field.type = field_type + field_list.append(struct_field) + + else: + valid = False + break + else: + valid = False + break + + if valid: + header = TypeHeader() + header.align = alignment + header.size = offset + resolved = GoTypeStruct(header=header, version=self.__version) + resolved.fields = field_list + return resolved + + def __map_to_type(self, map_repr: str) -> Union[GoTypeMap, None]: + """ + Parses the string representation of a Go map type. The string must start with "map[". + + :param map_repr str: The string representation of a Go map type. + :return Union[GoTypeMap, None]: Returns a GoTypeMap if the provided string is valid, otherwise None. + """ + resolved: Union[GoTypeMap, None] = None + body = map_repr[4:] + # Track level of [] nestedness. + level = 1 + + i = 0 + while i < len(body): + if body[i] == "[": + level += 1 + elif body[i] == "]": + level -= 1 + if level == 0: + break + i += 1 + + if i < len(body) - 1: + key_string = body[:i] + val_string = body[i + 1 :] + key_type = self.string_to_type(key_string) + val_type = self.string_to_type(val_string) + if key_type is not None and val_type is not None: + header = TypeHeader() + header.align = self.__ptr_size + header.size = self.__ptr_size + resolved = GoTypeMap(header=header, version=self.__version) + resolved.key_type = key_type + resolved.child_type = val_type + + (go_min_version, _) = self.__version + if go_min_version < 24: + # Old map type. + bucket_str = ( + f"struct {{ topbits [8]uint8; keys [8]{key_string}; " + f"elems [8]{val_string}; overflow uintptr }}" + ) + else: + # New (Swiss) map type. + bucket_str = f"struct {{ ctrl uint64; slots [8]struct {{ key {key_string}; elem {val_string} }} }}" + resolved.bucket_type = self.string_to_type(bucket_str) + return resolved + + def __construct_from_simple(self, simple_type: SimpleType) -> GoType: + """ + Converts an entry in the simple map into an actual GoType. + + :param SimpleType simple_triple: The type, size and alignment. + :return GoType: A GoType with the provided characteristics. + """ + header = TypeHeader() + header.align = simple_type.alignment + header.size = simple_type.size + return simple_type.go_type(header=header, version=self.__version) + + def string_to_type(self, type_repr: str) -> Union[GoType, None]: + """ + Parses the string representation of any Go type. + This is not a fully-compliant parser: for example, do not use characters such as "{}[];" in struct field names. + + :param type_repr str: The string representation of a Go type. + :return Union[GoType, None]: Returns a GoType object if the provided string is valid, otherwise None. + """ + + resolved: Union[GoType, None] = None + + if LLEFState.go_state.moduledata_info is not None: + # First check if easily available from the binary: + resolved = self.__name_to_type.get(type_repr) + if resolved is None: + # If not, parse it ourselves. + + simple_triple = self.__simple_map.get(type_repr) + if simple_triple is not None: + # Simple data types. + resolved = self.__construct_from_simple(simple_triple) + + else: + # Complex data types. + if type_repr.startswith("[]"): + resolved = self.__slice_to_type(type_repr) + + elif type_repr.startswith("["): + resolved = self.__array_to_type(type_repr) + + elif type_repr.startswith("*"): + resolved = self.__pointer_to_type(type_repr) + + elif type_repr.startswith("struct"): + resolved = self.__struct_to_type(type_repr) + + elif type_repr.startswith("map["): + resolved = self.__map_to_type(type_repr) + + elif type_repr.startswith("func") or type_repr.startswith("chan"): + # We don't unpack these types, so just leave them as raw pointers. + resolved = self.__construct_from_simple(self.__simple_map["uintptr"]) + + elif type_repr.startswith("interface"): + pass + + return resolved + + def __init__(self, type_structs: dict[int, GoType]) -> None: + """ + Set up internal state, such as the map from type names to types. + + :param dict[int, GoType] type_structs: The type_structs object from moduledata_info. + """ + self.__version = LLEFState.go_state.pclntab_info.version_bounds + self.__ptr_size = LLEFState.go_state.pclntab_info.ptr_size + self.__name_to_type: dict[str, GoType] = {} + for go_type in type_structs.values(): + self.__name_to_type[go_type.header.name] = go_type + + self.__simple_map = { + "bool": SimpleType(go_type=GoTypeBool, size=1, alignment=1), + "complex64": SimpleType(go_type=GoTypeComplex64, size=8, alignment=4), + "complex128": SimpleType(go_type=GoTypeComplex128, size=16, alignment=8), + "float32": SimpleType(go_type=GoTypeFloat32, size=4, alignment=4), + "float64": SimpleType(go_type=GoTypeFloat64, size=8, alignment=8), + "int": SimpleType(go_type=GoTypeInt, size=self.__ptr_size, alignment=self.__ptr_size), + "int8": SimpleType(go_type=GoTypeInt8, size=1, alignment=1), + "int16": SimpleType(go_type=GoTypeInt16, size=2, alignment=2), + "int32": SimpleType(go_type=GoTypeInt32, size=4, alignment=4), + "int64": SimpleType(go_type=GoTypeInt64, size=8, alignment=8), + "uint": SimpleType(go_type=GoTypeUint, size=self.__ptr_size, alignment=self.__ptr_size), + "uint8": SimpleType(go_type=GoTypeUint8, size=1, alignment=1), + "uint16": SimpleType(go_type=GoTypeUint16, size=2, alignment=2), + "uint32": SimpleType(go_type=GoTypeUint32, size=4, alignment=4), + "uint64": SimpleType(go_type=GoTypeUint64, size=8, alignment=8), + "string": SimpleType(go_type=GoTypeString, size=self.__ptr_size * 2, alignment=self.__ptr_size), + "uintptr": SimpleType(go_type=GoTypeUintptr, size=self.__ptr_size, alignment=self.__ptr_size), + "unsafe.Pointer": SimpleType(go_type=GoTypeUnsafePointer, size=self.__ptr_size, alignment=self.__ptr_size), + } diff --git a/common/golang/types.py b/common/golang/types.py new file mode 100644 index 0000000..ae4ea39 --- /dev/null +++ b/common/golang/types.py @@ -0,0 +1,1400 @@ +"""Python version of the Go typing system. A little-endian architecture is assumed throughout this file.""" + +import struct +from dataclasses import dataclass +from typing import Union + +from lldb import SBError, SBProcess + +from common.constants import pointer +from common.golang.constants import ( + GO_ENTROPY_SOFTNESS, + GO_MAX_SLICE_EXTRACT, + GO_MAX_STRING_READ, + GO_MAX_SWISSMAP_DIRS, + GO_TUNE_SLICE_RATE, + GO_TUNE_SLICE_THRESHOLD, + GO_TUNE_STRING_RATE, + GO_TUNE_STRING_THRESHOLD, +) +from common.golang.data import ( + Confidence, + GoData, + GoDataArray, + GoDataBad, + GoDataBool, + GoDataComplex, + GoDataFloat, + GoDataInteger, + GoDataMap, + GoDataPointer, + GoDataSlice, + GoDataString, + GoDataStruct, + GoDataUnparsed, +) +from common.golang.util_stateless import entropy, rate_candidate_length, read_varint + + +class TypeHeader: + """ + Used as a data structure to store common information present for every possible type. + """ + + size: int + ptrbytes: int # Number of (prefix) bytes in the type that can contain pointers. + t_hash: int # Hash of type. + tflag: int # Extra type information flags. + align: int # Alignment of variable with this type. + fieldalign: int # Alignment of struct field with this type. + kind: int # Enumeration for the type. + name: str # Name for type in string form. + + def __str__(self) -> str: + return ( + f"(size={self.size} ptrbytes={self.ptrbytes} hash={hex(self.t_hash)} tflag={self.tflag} align={self.align}" + f" fieldalign={self.fieldalign} kind={hex(self.kind & 31)} name='{self.name}')" + ) + + +@dataclass(frozen=True) +class PopulateInfo: + """ + Read-only parameters for populating types using populate(). + """ + + # Start of the 'types' section pointed to by the Go moduledata struct. + types: pointer + # End of the 'types' section pointed to by the Go moduledata struct. + etypes: pointer + ptr_size: int + ptr_specifier: str # for struct.unpack, e.g. Q for ptr_size == 8 bytes and L for 4 bytes. + + +@dataclass(frozen=True) +class ExtractInfo: + """ + Reduce length of extract_at() signature by packaging some read-only parameters in a struct. + """ + + proc: SBProcess + ptr_size: int + type_structs: dict[int, "GoType"] + + +def safe_read_unsigned(info: ExtractInfo, addr: pointer, size: int) -> Union[int, None]: + """ + Wraps proc.ReadUnsignedFromMemory to avoid internal LLDB errors when parameters are out of bounds. + Uses the same endianness as LLDB understands the target to be using. + + :param ExtractInfo info: Contains the process to perform the read on, and the pointer size. + :param int addr: The address to begin reading at. + :param int size: The number of bytes to read as the unsigned integer. + :return Union[int, None]: If the operation succeeded, returns the integer. Else None. + """ + max_pointer_value = 1 << (info.ptr_size * 8) + if addr >= 0 and size > 0 and addr + size <= max_pointer_value: + err = SBError() + value = info.proc.ReadUnsignedFromMemory(addr, size, err) + if err.Success(): + return value + return None + + +def safe_read_bytes(info: ExtractInfo, addr: pointer, size: int) -> Union[bytes, None]: + """ + Wraps proc.ReadMemory to avoid internal LLDB errors when parameters are out of bounds. + + :param ExtractInfo info: Contains the process to perform the read on, and the pointer size. + :param int addr: The address to begin reading at. + :param int size: The number of bytes to read. + :return Union[bytes, None]: If the operation succeeded, returns the byte string. Else None. + """ + max_pointer_value = 1 << (info.ptr_size * 8) + if addr >= 0 and size > 0 and addr + size <= max_pointer_value: + err = SBError() + buffer = info.proc.ReadMemory(addr, size, err) + if err.Success() and buffer is not None: + return buffer + return None + + +class GoType: + """ + The base class from which all Python versions of Go types inherit. + """ + + header: TypeHeader + version: tuple[int, int] + + # The following pattern occurs several times for different purposes in different type structs, but "child" is + # common enough that we put it here as an example. + # ..._addr and ..._type take default values 0 and None respectively. Upon populate(), ..._addr may get set to + # a non-zero address. This indicates that, at a later time, code should come along and set ..._type to the + # corresponding GoType as looked up in the type_structs dictionary. We can't set it now as it might not exist yet! + child_addr: pointer + child_type: Union["GoType", None] + + def __init__(self, header: TypeHeader, version: tuple[int, int]) -> None: + self.header = header + self.child_addr = 0 # indicate none until told otherwise. + self.child_type = None + self.version = version + + def populate(self, type_section: bytes, offset: pointer, info: PopulateInfo) -> Union[list[int], None]: + """ + Overridden by complex datatypes: reads further data included after the header to populate type-specific fields + with extra information. + + :param bytes type_section: Slice of program memory that the ModuleData structure refers to as the type section. + :param pointer offset: The offset in the section that immediately follows the header for this type. + :param PopulateInfo info: Packaged properties of the binary that we need to correctly continue parsing. + :return Union[list[int], None]: If extra parsing succeeds, returns a list of pointers to other type information + structures that we may need to parse recursively. Otherwise None. + """ + return [] + + def fixup_types(self, type_structs: dict[int, "GoType"]) -> None: + """ + Overridden by complex datatypes: is called on each type after a full type_structs dictionary has been built. + Allows them to populate any ..._type fields by looking up ..._addr addresses. + + :param dict[int, GoType] type_structs: The completed mapping from type information structure address to + subclasses of GoType. + """ + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + """ + Overriden by most classes: reads the memory from an address to attempt to extract the object of this type, + and all of its children. The children are encapsulated in the GoData object. Also calculates a heuristic, which + is a confidence level of the address provided actually being of this type. + + :param SBProcess proc: The LLDB process object currently being worked on. + :param pointer addr: The memory address to start unpacking at. + :param int ptr_size: The size of a pointer in bytes. + :param set[pointer] dereferenced_pointers: A set, initially empty, + to keep track of pointers already dereferenced. + :param int depth: How deeply down nested structures/arrays/slices to unpack. + :return GoData: A Python object replicating the Go object after unpacking. + """ + + return GoDataPointer(heuristic=Confidence.CERTAIN.to_float(), address=addr) + + def get_underlying_type(self, depth: int) -> str: + """ + Implemented by each type: Returns the underlying type for this GoType. + + :param int depth: Avoid infinite recursion by tracking how deeply we've dereferenced. + :return str: The type represented by this GoType, in Go syntax, without using type synonyms. + """ + return "?" + + @classmethod + def make_from(cls, header: TypeHeader, version: tuple[int, int]) -> Union["GoType", None]: + """ + Selects the correct subclass of GoType based on the Kind, and returns an initialised object of it. + + :param TypeHeader header: An already-completed TypeHeader, containing the Kind attribute. + :return Union[GoType, None]: If the Kind was valid, an initialised object for the subclass of GoType. + Otherwise None. + """ + enum: dict[int, type[GoType]] = { + 0: GoTypeInvalid, + 1: GoTypeBool, + 2: GoTypeInt, + 3: GoTypeInt8, + 4: GoTypeInt16, + 5: GoTypeInt32, + 6: GoTypeInt64, + 7: GoTypeUint, + 8: GoTypeUint8, + 9: GoTypeUint16, + 10: GoTypeUint32, + 11: GoTypeUint64, + 12: GoTypeUintptr, + 13: GoTypeFloat32, + 14: GoTypeFloat64, + 15: GoTypeComplex64, + 16: GoTypeComplex128, + 17: GoTypeArray, + 18: GoTypeChan, + 19: GoTypeFunc, + 20: GoTypeInterface, + 21: GoTypeMap, + 22: GoTypePointer, + 23: GoTypeSlice, + 24: GoTypeString, + 25: GoTypeStruct, + 26: GoTypeUnsafePointer, + } + + # Type enum is only lower 5 bits. + subtype = enum.get(header.kind & 0b11111) + if subtype: + # calls GoType.__init__ since subtype does not redefine. + return subtype(header, version) + return None + + def __str__(self) -> str: + return self.__class__.__name__ + str(self.header) + + +class GoTypeInvalid(GoType): + def get_underlying_type(self, depth: int) -> str: + return "invalid" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + return GoDataBad(heuristic=Confidence.JUNK.to_float()) + + +class GoTypeBool(GoType): + def get_underlying_type(self, depth: pointer) -> str: + return "bool" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + extracted: Union[GoData, None] = None + + val = safe_read_unsigned(info, addr, 1) + if val is not None: + if val == 1: + extracted = GoDataBool(heuristic=Confidence.CERTAIN.to_float(), value=True) + elif val == 0: + extracted = GoDataBool(heuristic=Confidence.CERTAIN.to_float(), value=False) + + if extracted is None: + extracted = GoDataBad(heuristic=Confidence.JUNK.to_float()) + return extracted + + +class GoTypeInt(GoType): + def get_underlying_type(self, depth: int) -> str: + return "int" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + val = safe_read_unsigned(info, addr, info.ptr_size) + if val is not None: + sign_bit = 1 << (info.ptr_size - 1) + # convert unsigned to signed + val -= (val & sign_bit) << 1 + return GoDataInteger(heuristic=Confidence.CERTAIN.to_float(), value=val) + return GoDataBad(heuristic=Confidence.JUNK.to_float()) + + +class GoTypeInt8(GoType): + def get_underlying_type(self, depth: int) -> str: + return "int8" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + val = safe_read_unsigned(info, addr, 1) + if val is not None: + sign_bit = 1 << 7 + # convert unsigned to signed + val -= (val & sign_bit) << 1 + return GoDataInteger(heuristic=Confidence.CERTAIN.to_float(), value=val) + return GoDataBad(heuristic=Confidence.JUNK.to_float()) + + +class GoTypeInt16(GoType): + def get_underlying_type(self, depth: int) -> str: + return "int16" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + val = safe_read_unsigned(info, addr, 2) + if val is not None: + sign_bit = 1 << 15 + # convert unsigned to signed + val -= (val & sign_bit) << 1 + return GoDataInteger(heuristic=Confidence.CERTAIN.to_float(), value=val) + return GoDataBad(heuristic=Confidence.JUNK.to_float()) + + +class GoTypeInt32(GoType): + def get_underlying_type(self, depth: int) -> str: + return "int32" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + val = safe_read_unsigned(info, addr, 4) + if val is not None: + sign_bit = 1 << 31 + # convert unsigned to signed + val -= (val & sign_bit) << 1 + return GoDataInteger(heuristic=Confidence.CERTAIN.to_float(), value=val) + return GoDataBad(heuristic=Confidence.JUNK.to_float()) + + +class GoTypeInt64(GoType): + def get_underlying_type(self, depth: int) -> str: + return "int64" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + val = safe_read_unsigned(info, addr, 8) + if val is not None: + sign_bit = 1 << 63 + # convert unsigned to signed + val -= (val & sign_bit) << 1 + return GoDataInteger(heuristic=Confidence.CERTAIN.to_float(), value=val) + return GoDataBad(heuristic=Confidence.JUNK.to_float()) + + +class GoTypeUint(GoType): + def get_underlying_type(self, depth: int) -> str: + return "uint" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + val = safe_read_unsigned(info, addr, info.ptr_size) + if val is not None: + return GoDataInteger(heuristic=Confidence.CERTAIN.to_float(), value=val) + return GoDataBad(heuristic=Confidence.JUNK.to_float()) + + +class GoTypeUint8(GoType): + def get_underlying_type(self, depth: int) -> str: + return "uint8" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + val = safe_read_unsigned(info, addr, 1) + if val is not None: + return GoDataInteger(heuristic=Confidence.CERTAIN.to_float(), value=val) + return GoDataBad(heuristic=Confidence.JUNK.to_float()) + + +class GoTypeUint16(GoType): + def get_underlying_type(self, depth: int) -> str: + return "uint16" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + val = safe_read_unsigned(info, addr, 2) + if val is not None: + return GoDataInteger(heuristic=Confidence.CERTAIN.to_float(), value=val) + return GoDataBad(heuristic=Confidence.JUNK.to_float()) + + +class GoTypeUint32(GoType): + def get_underlying_type(self, depth: int) -> str: + return "uint32" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + val = safe_read_unsigned(info, addr, 4) + if val is not None: + return GoDataInteger(heuristic=Confidence.CERTAIN.to_float(), value=val) + return GoDataBad(heuristic=Confidence.JUNK.to_float()) + + +class GoTypeUint64(GoType): + def get_underlying_type(self, depth: int) -> str: + return "uint64" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + val = safe_read_unsigned(info, addr, 8) + if val is not None: + return GoDataInteger(heuristic=Confidence.CERTAIN.to_float(), value=val) + return GoDataBad(heuristic=Confidence.JUNK.to_float()) + + +class GoTypeUintptr(GoType): + def get_underlying_type(self, depth: int) -> str: + return "uintptr" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + val = safe_read_unsigned(info, addr, info.ptr_size) + if val is not None: + return GoDataPointer(heuristic=Confidence.CERTAIN.to_float(), address=val) + return GoDataBad(heuristic=Confidence.JUNK.to_float()) + + +class GoTypeFloat32(GoType): + def get_underlying_type(self, depth: int) -> str: + return "float32" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + data = safe_read_bytes(info, addr, 4) + if data is not None: + extracted: tuple[float] = struct.unpack(" str: + return "float64" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + data = safe_read_bytes(info, addr, 8) + if data is not None: + extracted: tuple[float] = struct.unpack(" str: + return "complex64" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + data = safe_read_bytes(info, addr, 8) + if data is not None: + extracted: tuple[float, float] = struct.unpack(" str: + return "complex128" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + data = safe_read_bytes(info, addr, 16) + if data is not None: + extracted: tuple[float, float] = struct.unpack(" Union[list[int], None]: + (sub_elem, sup_slice, this_len) = struct.unpack_from("<" + info.ptr_specifier * 3, type_section, offset) + self.child_addr = sub_elem + self.length = this_len + return [sub_elem, sup_slice] + + def fixup_types(self, type_structs: dict[int, "GoType"]) -> None: + self.child_type = type_structs.get(self.child_addr, None) + + def get_underlying_type(self, depth: int) -> str: + if depth > 0: + subtype = "?" + if self.child_type is not None: + subtype = self.child_type.get_underlying_type(depth - 1) + return f"[{self.length}]{subtype}" + else: + return self.header.name + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + extracted: Union[GoData, None] = None + + if self.child_type is not None: + align = self.child_type.header.align + elem_size = ((self.child_type.header.size + align - 1) // align) * align + + if addr % align == 0 and elem_size * self.length == self.header.size: + if depth > 0: + + values = [] + heuristic_sum = 0.0 + if self.length > 0: + valid = True + for i in range(self.length): + elem = self.child_type.extract_at( + info, addr + i * elem_size, dereferenced_pointers, depth - 1 + ) + if isinstance(elem, GoDataBad): + # then the memory doesn't actually exist for this element, + # so the memory for the array does not exist either. + valid = False + break + + heuristic_sum += elem.heuristic + values.append(elem) + + if valid: + extracted = GoDataArray(heuristic=heuristic_sum / self.length, contents=values) + + else: + extracted = GoDataArray(heuristic=Confidence.CERTAIN.to_float(), contents=[]) + + else: + extracted = GoDataUnparsed(heuristic=Confidence.CERTAIN.to_float(), address=addr) + + if extracted is None: + extracted = GoDataBad(heuristic=Confidence.JUNK.to_float()) + return extracted + + +class GoTypeChan(GoType): + direction: int + + def populate(self, type_section: bytes, offset: int, info: PopulateInfo) -> Union[list[int], None]: + (sub_elem, direction) = struct.unpack_from("<" + info.ptr_specifier * 2, type_section, offset) + self.child_addr = sub_elem + self.direction = direction + return [sub_elem] + + def fixup_types(self, type_structs: dict[int, "GoType"]) -> None: + self.child_type = type_structs.get(self.child_addr, None) + + def get_underlying_type(self, depth: int) -> str: + if depth > 0: + subtype = "?" + if self.child_type is not None: + subtype = self.child_type.get_underlying_type(depth - 1) + + base_name = "chan" + if self.direction == 1: + # Receiving channel + base_name = "<-" + base_name + elif self.direction == 2: + # Sending channel + base_name = base_name + "<-" + + return f"{base_name} {subtype}" + else: + return self.header.name + + +class GoTypeFunc(GoType): + input_addrs: list[int] + input_types: list[Union[GoType, None]] # the _types lists will be instantiated and filled later. + output_addrs: list[int] + output_types: list[Union[GoType, None]] + is_variadic: bool + + def populate(self, type_section: bytes, offset: int, info: PopulateInfo) -> Union[list[int], None]: + # the size of param count fields and the uncommon offset addition is NOT pointer size dependent. + (num_param_in, num_param_out) = struct.unpack_from(" None: + self.input_types = [] + for addr in self.input_addrs: + self.input_types.append(type_structs.get(addr, None)) + + self.output_types = [] + for addr in self.output_addrs: + self.output_types.append(type_structs.get(addr, None)) + + def get_underlying_type(self, depth: int) -> str: + if depth > 0: + inputs = [] + for t in self.input_types: + if t is not None: + inputs.append(t.get_underlying_type(depth - 1)) + else: + inputs.append("?") + if self.is_variadic: + inputs[-1] = "..." + inputs[-1].removeprefix("[]") + + outputs = [] + for t in self.output_types: + if t is not None: + outputs.append(t.get_underlying_type(depth - 1)) + else: + outputs.append("?") + + build = f"func({', '.join(inputs)})" + output_str = ", ".join(outputs) + if len(outputs) == 1: + build += f" {output_str}" + elif len(outputs) > 1: + build += f" ({output_str})" + else: + build = self.header.name + return build + + +class GoTypeInterfaceMethod: + name: str + type_addr: pointer + type: Union[GoType, None] + + def __init__(self, name: str, type_addr: pointer): + self.type_addr = type_addr + self.name = name + self.type = None + + +class GoTypeInterface(GoType): + methods: list[GoTypeInterfaceMethod] + + def populate(self, type_section: bytes, offset: int, info: PopulateInfo) -> Union[list[pointer], None]: + self.methods = [] + (go_min_version, _) = self.version + (methods_base, methods_len) = struct.unpack_from( + "<" + info.ptr_specifier * 2, type_section, offset + info.ptr_size + ) + # Each method structure in the methods table is a struct of two 32-bit integers. + for i in range(methods_len): + imethod_ptr = methods_base + i * 8 + if info.types <= imethod_ptr < info.etypes: + imethod_offset = imethod_ptr - info.types + (name_off, type_off) = struct.unpack_from("H", type_section, name_off) + name_off += 2 + else: + length, name_off = read_varint(type_section, name_off) + + name = type_section[name_off : name_off + length].decode("utf-8", "replace") + + imethod = GoTypeInterfaceMethod(name=name, type_addr=info.types + type_off) + self.methods.append(imethod) + + return list(map(lambda x: x.type_addr, self.methods)) + + def fixup_types(self, type_structs: dict[pointer, "GoType"]) -> None: + for method in self.methods: + method.type = type_structs.get(method.type_addr) + + def get_underlying_type(self, depth: int) -> str: + if depth > 0: + build = "" + for method in self.methods: + if method.type is not None: + func_name = method.type.get_underlying_type(depth - 1) + + # Replace func with the actual function name. + build += f"{method.name}{func_name.removeprefix('func')}; " + build = build.removesuffix("; ") + if len(build) > 0: + build = f"interface {{ {build} }}" + else: + build = "interface {}" + else: + build = self.header.name + return build + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + extracted: Union[GoData, None] = None + + type_ptr: Union[int, None] = None + fail_nicely = False # Set to True if we get a successful initial read (so the memory actually exists). + + if len(self.methods) == 0: + # Empty interface type. This is two ptr_sized integers: first a type pointer, then the data pointer. + type_ptr = safe_read_unsigned(info, addr, info.ptr_size) + if type_ptr is not None: + fail_nicely = True + else: + # Non-empty interface type. This is two ptr_sized integers: first an ITab pointer, then the data pointer. + # Concrete (dynamic) type is available inside ITab. + itab_ptr = safe_read_unsigned(info, addr, info.ptr_size) + if itab_ptr is not None: + fail_nicely = True + type_ptr = safe_read_unsigned(info, itab_ptr + info.ptr_size, info.ptr_size) + + # Treat this like dereferencing a typed pointer. + if type_ptr is not None: + header = TypeHeader() + extractor = GoTypePointer(header=header, version=self.version) + extractor.child_type = info.type_structs.get(type_ptr) + extracted = extractor.extract_at(info, addr + info.ptr_size, dereferenced_pointers, depth) + + if extracted is None: + if fail_nicely: + extracted = GoDataPointer(heuristic=Confidence.LOW.to_float(), address=addr + info.ptr_size) + else: + extracted = GoDataBad(heuristic=Confidence.JUNK.to_float()) + return extracted + + +class GoTypeMap(GoType): + + key_addr: pointer + key_type: Union[GoType, None] + bucket_addr: pointer + bucket_type: Union[GoType, None] + + def populate(self, type_section: bytes, offset: int, info: PopulateInfo) -> Union[list[pointer], None]: + (self.key_addr, self.child_addr, self.bucket_addr) = struct.unpack_from( + "<" + info.ptr_specifier * 3, type_section, offset + ) + self.key_type = None + self.bucket_type = None + return [self.key_addr, self.child_addr, self.bucket_addr] + + def fixup_types(self, type_structs: dict[pointer, "GoType"]) -> None: + self.key_type = type_structs.get(self.key_addr, None) + self.child_type = type_structs.get(self.child_addr, None) + self.bucket_type = type_structs.get(self.bucket_addr, None) + + def get_underlying_type(self, depth: int) -> str: + key_str = "?" + if self.key_type is not None: + key_str = self.key_type.get_underlying_type(depth) + val_str = "?" + if self.child_type is not None: + val_str = self.child_type.get_underlying_type(depth) + return f"map[{key_str}]{val_str}" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + extracted: Union[GoData, None] = None + + if self.bucket_type is not None: + (go_min_version, _) = self.version + # Minimum version is only lifted to 24 if we are sure the new map implementation is being used + # (the programmer can choose to use the old version even in 1.24). + parser: Union[SwissMapParser, NoSwissMapParser] + if go_min_version < 24: + parser = NoSwissMapParser(info, self.bucket_type, dereferenced_pointers, self.version) + else: + parser = SwissMapParser(info, self.bucket_type, dereferenced_pointers) + extracted = parser.parse(addr, depth) + # changes to dereferenced_pointers are still present, since it was passed by reference. + + if extracted is None: + extracted = GoDataBad(heuristic=Confidence.JUNK.to_float()) + return extracted + + +class GoTypePointer(GoType): + def populate(self, type_section: bytes, offset: int, info: PopulateInfo) -> Union[list[int], None]: + (sub_elem,) = struct.unpack_from("<" + info.ptr_specifier, type_section, offset) + self.child_addr = sub_elem + return [sub_elem] + + def fixup_types(self, type_structs: dict[int, "GoType"]) -> None: + self.child_type = type_structs.get(self.child_addr, None) + + def get_underlying_type(self, depth: int) -> str: + subtype = "?" + if self.child_type is not None: + if depth > 0: + subtype = self.child_type.get_underlying_type(depth - 1) + else: + subtype = self.child_type.header.name + return f"*{subtype}" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + extracted: Union[GoData, None] = None + + if self.child_type: + child_ptr = safe_read_unsigned(info, addr, info.ptr_size) + if child_ptr is not None: + if child_ptr > 0: + if child_ptr not in dereferenced_pointers: + dereferenced_pointers.add(child_ptr) + # changes to dereferenced_pointers are reflected everywhere, so we'll never dereference again + # in this extraction. + # this is good because we can reduce duplication of displayed information. + dereferenced = self.child_type.extract_at(info, child_ptr, dereferenced_pointers, depth) + if not isinstance(dereferenced, GoDataBad): + extracted = dereferenced + else: + # Then this pointer is not of this type - either memory does not exist, or data is illegal. + extracted = GoDataPointer(heuristic=Confidence.JUNK.to_float(), address=child_ptr) + else: + # Circular references. Slightly downgrade confidence. + extracted = GoDataUnparsed(heuristic=Confidence.HIGH.to_float(), address=child_ptr) + else: + # A valid, but null, pointer. Of course these come up - but downgrade the confidence. + extracted = GoDataPointer(heuristic=Confidence.MEDIUM.to_float(), address=0) + + if extracted is None: + extracted = GoDataBad(heuristic=Confidence.JUNK.to_float()) + return extracted + + +class GoTypeSlice(GoType): + def populate(self, type_section: bytes, offset: int, info: PopulateInfo) -> Union[list[int], None]: + (sub_elem,) = struct.unpack_from("<" + info.ptr_specifier, type_section, offset) + self.child_addr = sub_elem + return [sub_elem] + + def fixup_types(self, type_structs: dict[int, "GoType"]) -> None: + self.child_type = type_structs.get(self.child_addr, None) + + def get_underlying_type(self, depth: int) -> str: + subtype = "?" + if self.child_type is not None: + subtype = self.child_type.get_underlying_type(depth) + return f"[]{subtype}" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + extracted: Union[GoData, None] = None + + if self.child_type: + align = self.child_type.header.align + elem_size = ((self.child_type.header.size + align - 1) // align) * align + + base = safe_read_unsigned(info, addr, info.ptr_size) + length = safe_read_unsigned(info, addr + info.ptr_size, info.ptr_size) + cap = safe_read_unsigned(info, addr + 2 * info.ptr_size, info.ptr_size) + if base is not None and length is not None and cap is not None and base % align == 0 and cap >= length: + values = [] + + if cap > 0: + # The relationship between length and capacity can present a useful heuristic. + # Many Go slices have length = capacity, or the length is very small, or + # it is >= 50% of the capacity since Go doubles when reallocating. + length_score = min( + rate_candidate_length(length, GO_TUNE_SLICE_THRESHOLD, GO_TUNE_SLICE_RATE), + rate_candidate_length(cap, 2 * GO_TUNE_SLICE_THRESHOLD, 2 * GO_TUNE_SLICE_RATE), + ) + + if depth > 0: + if length > 0: + heuristic_sum = 0.0 + + # Don't extract a huge number of elements! + num_extract = min(length, GO_MAX_SLICE_EXTRACT) + valid = True + for i in range(num_extract): + elem = self.child_type.extract_at( + info, base + i * elem_size, dereferenced_pointers, depth - 1 + ) + if isinstance(elem, GoDataBad): + valid = False + break + + heuristic_sum += elem.heuristic + values.append(elem) + + if valid: + # Successful parsing here. + score = (length_score + (heuristic_sum / num_extract)) / 2 + extracted = GoDataSlice( + heuristic=score, base=base, length=length, capacity=cap, contents=values + ) + else: + # The underlying memory is bad. But we'll still communicate extracted slice + # information, (at a low confidence). + extracted = GoDataSlice( + heuristic=Confidence.LOW.to_float(), + base=base, + length=length, + capacity=cap, + contents=[], + ) + else: + # Length of 0. + extracted = GoDataSlice( + heuristic=length_score, base=base, length=length, capacity=cap, contents=[] + ) + else: + # Ran out of depth. + extracted = GoDataUnparsed(heuristic=length_score, address=base) + else: + # Capacity of 0 is quite unusual. + extracted = GoDataSlice( + heuristic=Confidence.LOW.to_float(), base=base, length=length, capacity=cap, contents=[] + ) + + if extracted is None: + extracted = GoDataBad(heuristic=Confidence.JUNK.to_float()) + return extracted + + +class GoTypeString(GoType): + def get_underlying_type(self, depth: int) -> str: + return "string" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + extracted: Union[GoData, None] = None + + str_start = safe_read_unsigned(info, addr, info.ptr_size) + length = safe_read_unsigned(info, addr + info.ptr_size, info.ptr_size) + + if str_start is not None and length is not None: + if length > 0: + score = rate_candidate_length(length, GO_TUNE_STRING_THRESHOLD, GO_TUNE_STRING_RATE) + + # Only extract the start of a very long string. + num_extract = min(length, GO_MAX_STRING_READ) + data = safe_read_bytes(info, str_start, num_extract) + if data is not None: + decoded = data.decode("utf-8", "replace") + str_len = len(decoded) + num_printable = 0 + for char in decoded: + if char.isprintable(): + num_printable += 1 + + if str_len > 0: + score = (score + num_printable / str_len) / 2 + else: + # Then decoding just went utterly wrong. + score /= 2 + extracted = GoDataString(heuristic=score, base=str_start, length=length, contents=data) + + else: + # the underlying data is bad. severely tank the heuristic, but keep base and length info. + extracted = GoDataString(heuristic=score * 0.2, base=str_start, length=length, contents=b"") + else: + # Since Go strings are immutable, it's highly unlikely the empty string comes up that much. + # This is probably something else. + extracted = GoDataString(heuristic=Confidence.LOW.to_float(), base=str_start, length=0, contents=b"") + + if extracted is None: + extracted = GoDataBad(heuristic=Confidence.JUNK.to_float()) + return extracted + + +class GoTypeStructField: + offset: int + name: str + type_addr: pointer + type: Union[GoType, None] + + def __init__(self, offset: int, name: str, type_addr: pointer): + self.offset = offset + self.type_addr = type_addr + self.name = name + self.type = None + + +class GoTypeStruct(GoType): + fields: list[GoTypeStructField] + + def populate(self, type_section: bytes, offset: int, info: PopulateInfo) -> Union[list[pointer], None]: + (go_min_version, go_max_version) = self.version + (_, fields_addr, fields_len) = struct.unpack_from("<" + info.ptr_specifier * 3, type_section, offset) + self.fields = [] + + for i in range(fields_len): + # Each field struct is 3 pointers wide. + addr = fields_addr + i * 3 * info.ptr_size + if info.types <= addr < info.etypes: + field_struct_offset = addr - info.types + (field_name_ptr, field_type, field_offset) = struct.unpack_from( + "<" + info.ptr_specifier * 3, type_section, field_struct_offset + ) + + field_name_ptr = field_name_ptr - info.types + 1 + + if go_min_version <= 16: + # In this case, the length is big-endian no matter the architecture. + (length,) = struct.unpack_from(">H", type_section, field_name_ptr) + field_name_ptr += 2 + else: + length, field_name_ptr = read_varint(type_section, field_name_ptr) + + name = type_section[field_name_ptr : field_name_ptr + length].decode("utf-8", "replace") + + # And in Go 1.9 through Go 1.18, the field_offset is stored left-shifted by one. + if go_min_version >= 9 and go_max_version <= 18: + field_offset >>= 1 + field = GoTypeStructField(field_offset, name, field_type) + self.fields.append(field) + + # Ensure we list fields in order of ascending offset. + self.fields.sort(key=lambda x: x.offset) + return list(map(lambda x: x.type_addr, self.fields)) + + def fixup_types(self, type_structs: dict[int, "GoType"]) -> None: + for field in self.fields: + field.type = type_structs.get(field.type_addr, None) + + def get_underlying_type(self, depth: int) -> str: + build = "" + if depth > 0: + for field in self.fields: + type_str = "?" + if field.type is not None: + type_str = field.type.get_underlying_type(depth - 1) + build += f"{field.name} {type_str}; " + + build = build.removesuffix("; ") + if len(build) > 0: + build = f"struct {{ {build} }}" + else: + build = "struct {}" + else: + build = self.header.name + + return build + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + extracted: Union[GoData, None] = None + store = [] + + if depth > 0: + if len(self.fields) > 0: + heuristic_sum = 0.0 + + for field in self.fields: + value: GoData = GoDataBad(heuristic=Confidence.JUNK.to_float()) + if field.type is not None: + value = field.type.extract_at(info, addr + field.offset, dereferenced_pointers, depth - 1) + + if isinstance(value, GoDataBad): + # it may be uninitialised, while the previous fields in the struct contain good information. + # so just return what we've got so far. + break + + heuristic_sum += value.heuristic + store.append((field.name, value)) + + if len(store) > 0: + extracted = GoDataStruct(heuristic=heuristic_sum / len(store), fields=store) + else: + # Empty struct. + extracted = GoDataStruct(heuristic=Confidence.CERTAIN.to_float(), fields=[]) + else: + # Ran out of depth. + extracted = GoDataUnparsed(heuristic=Confidence.CERTAIN.to_float(), address=addr) + + if extracted is None: + extracted = GoDataBad(heuristic=Confidence.JUNK.to_float()) + return extracted + + +class GoTypeUnsafePointer(GoType): + def get_underlying_type(self, depth: int) -> str: + return "unsafe.Pointer" + + def extract_at(self, info: ExtractInfo, addr: pointer, dereferenced_pointers: set[pointer], depth: int) -> GoData: + child_ptr = safe_read_unsigned(info, addr, info.ptr_size) + if child_ptr is not None: + return GoDataPointer(heuristic=Confidence.CERTAIN.to_float(), address=child_ptr) + return GoDataBad(heuristic=Confidence.JUNK.to_float()) + + +class NoSwissMapParser: + """ + A single-use parser for pre-1.24 maps (non-Swiss). + """ + + info: ExtractInfo + version: tuple[int, int] + bucket_type: GoType + + # the seen-address set is always passed around by reference, so keep a reference here for easy access. + # In the unpacking of an entire object we never wish to unpack the same pointer twice (for brevity). + seen_pointers: set[int] + + def __init__( + self, info: ExtractInfo, bucket_type: GoType, dereferenced_pointers: set[pointer], version: tuple[int, int] + ): + self.info = info + self.bucket_type = bucket_type + self.seen_pointers = dereferenced_pointers + self.version = version + + def __parse_bucket( + self, nest_depth: int, bucket: GoDataStruct, overflow_depth: int + ) -> Union[list[tuple[GoData, GoData]], None]: + """ + Reads the contents of a single bucket, which could be from the main bucket array or an overflow bucket. + + :param int nest_depth: The further depth allowed when extracting the key and value objects. + :param GoDataStruct bucket: The unpacked bucket object. + :param int overflow_depth: The number of further overflow buckets that can be followed before giving up. + :return Union[list[tuple[GoData, GoData]], None]: If parsing succeeded, a list of pairs key/value. Else None. + """ + from_bucket: Union[list[tuple[GoData, GoData]], None] = None + accesser = dict(bucket.fields) + valid = True + try: + topbits = accesser["topbits"] + keys = accesser["keys"] + elems = accesser["elems"] + overflow = accesser["overflow"] + except KeyError: + valid = False + + # Check bucket structure is as expected. + if ( + valid + and isinstance(topbits, GoDataArray) + and isinstance(keys, GoDataArray) + and isinstance(elems, GoDataArray) + and isinstance(overflow, GoDataPointer) + ): + bucket_size = len(topbits.contents) + if bucket_size > 0 and len(keys.contents) == bucket_size and len(elems.contents) == bucket_size: + from_bucket = [] + for i in range(bucket_size): + header_byte_obj = topbits.contents[i] + key_obj = keys.contents[i] + elem_obj = elems.contents[i] + + # Check bucket structure is as expected. + if isinstance(header_byte_obj, GoDataInteger): + h = header_byte_obj.value + + # h-values 0, 1 and 4 mean an empty cell. + if h not in (0, 1, 4): + from_bucket.append((key_obj, elem_obj)) + + if overflow.address > 0 and self.bucket_type is not None: + overflow_bucket_object = self.bucket_type.extract_at( + self.info, overflow.address, self.seen_pointers, nest_depth + ) + if isinstance(overflow_bucket_object, GoDataStruct) and overflow_depth > 0: + from_overflow = self.__parse_bucket(nest_depth, overflow_bucket_object, overflow_depth - 1) + if from_overflow is not None: + from_bucket.extend(from_overflow) + + return from_bucket + + def __parse_bucket_list(self, nest_depth: int, buckets_object: GoData, confidence: float) -> Union[GoData, None]: + """ + Extracts data from the main buckets array of the map, and calculates an average heuristic. + + :param int nest_depth: The further depth allowed when extracting the key and value objects. + :param GoData buckets_object: The unpacked fixed-length array of bucket objects. + :param float confidence: The current confidence level from 0.0-1.0. + :return Union[GoData, None]: If parsing succeeds, returns a populated GoDataMap. Otherwise None. + """ + extracted: Union[GoData, None] = None + + if isinstance(buckets_object, GoDataArray): + bucket_list = buckets_object.contents + valid = True + entries: list[tuple[GoData, GoData]] = [] + for bucket in bucket_list: + if isinstance(bucket, GoDataStruct): + bucket_data = self.__parse_bucket(nest_depth, bucket, 8) + if bucket_data is None: + valid = False + break + entries.extend(bucket_data) + else: + valid = False + break + + # Empty map case was already handled. So presume bad data if len(entries) == 0. + if valid and len(entries) > 0: + heuristic_sum = 0.0 + for key, val in entries: + heuristic_sum += key.heuristic + heuristic_sum += val.heuristic + heuristic_avg = heuristic_sum / (2 * len(entries)) + heuristic = (3 * heuristic_avg + confidence) / 4 + + extracted = GoDataMap(heuristic=heuristic, entries=entries) + + return extracted + + def parse(self, addr: pointer, nest_depth: int) -> GoData: + """ + Extracts data from the header structure of the map. This is the first function to be called to parse a map. + + :param int addr: The memory address (resident in self.proc) to begin unpacking at. + :param int nest_depth: The further depth allowed when extracting the key and value objects. + :return GoData: Returns GoDataMap/GoDataUnparsed/GoDataBad depending on the situation. + """ + extracted: Union[GoData, None] = None + + count = safe_read_unsigned(self.info, addr, self.info.ptr_size) + log2_of_num_buckets = safe_read_unsigned(self.info, addr + self.info.ptr_size + 1, 1) + seed = safe_read_unsigned(self.info, addr + self.info.ptr_size + 4, 4) + buckets = safe_read_unsigned(self.info, addr + self.info.ptr_size + 8, self.info.ptr_size) + if count is not None and log2_of_num_buckets is not None and seed is not None and buckets is not None: + seed_bits = f"{seed:032b}" + + confidence = entropy(seed_bits) ** GO_ENTROPY_SOFTNESS + + if nest_depth > 0: + if count > 0: + if self.bucket_type is not None: + num_buckets = 2**log2_of_num_buckets + + # To aid in parsing, create a temporary type that is just used to extract + # this array of buckets. + buckets_type_header = TypeHeader() + buckets_type_header.align = self.bucket_type.header.align + buckets_type_header.size = self.bucket_type.header.size * num_buckets + + buckets_type = GoTypeArray(buckets_type_header, self.version) + buckets_type.length = num_buckets + buckets_type.child_type = self.bucket_type + # We'll lose 1 unit of depth in unpacking the array, + # and 1 unit in unpacking the bucket structs. + # Add 1 to compensate the loss of 2, so we unpack the children at nest_depth - 1. + buckets_object = buckets_type.extract_at(self.info, buckets, self.seen_pointers, nest_depth + 1) + + extracted = self.__parse_bucket_list(nest_depth, buckets_object, confidence) + + else: + # No entries in the map. + extracted = GoDataMap(heuristic=confidence, entries=[]) + else: + # Ran out of nest_depth. + extracted = GoDataUnparsed(heuristic=confidence, address=addr) + + if extracted is None: + extracted = GoDataBad(heuristic=Confidence.JUNK.to_float()) + return extracted + + +class SwissMapParser: + """ + A single-use parser for the new Go "Swiss" map type introduced in version 1.24. + """ + + info: ExtractInfo + ptr_specifier: str + group_type: GoType + + seen_pointers: set[int] + + def __init__(self, info: ExtractInfo, group_type: GoType, dereferenced_pointers: set[int]): + self.info = info + self.group_type = group_type + self.seen_pointers = dereferenced_pointers + if self.info.ptr_size == 4: + self.ptr_specifier = "I" + else: + self.ptr_specifier = "Q" + + def __unpack_slot(self, slot_obj: GoData) -> Union[tuple[GoData, GoData], None]: + """ + The structure of the slot struct in a group type is known. Unpack that slot to return the key/value pair. + + :param GoData slot_obj: The unpacked slot object. + :return Union[tuple[GoData, GoData], None]: A pair of key and value, or None if slot structure did not conform. + """ + if isinstance(slot_obj, GoDataStruct): + accesser = dict(slot_obj.fields) + key_obj = accesser.get("key") + elem_obj = accesser.get("elem") + if key_obj is not None and elem_obj is not None: + return (key_obj, elem_obj) + return None + + def __parse_group(self, group_ptr: pointer, nest_depth: int) -> Union[list[tuple[GoData, GoData]], None]: + """ + Given a pointer to a group (a.k.a. bucket), extract a list of valid entries. + + :param int group_ptr: The base address of the group relative to the address space for self.proc. + :param int nest_depth: The further depth allowed when extracting the key and value objects. + :return Union[list[tuple[GoData, GoData]], None]: If parsing succeeded, a list of key/value pairs. Else None. + """ + from_group: Union[list[tuple[GoData, GoData]], None] = None + # We lose 1 unit of depth unpacking the group, + # 1 unit unpacking the slots array and 1 unpacking an individual slot. + # Add 2 to compensate the loss of 3, so we unpack the children at nest_depth - 1. + group_obj = self.group_type.extract_at(self.info, group_ptr, self.seen_pointers, nest_depth + 2) + + # Check extracted group has expected structure. + if isinstance(group_obj, GoDataStruct): + accesser = dict(group_obj.fields) + ctrl_obj = accesser.get("ctrl") + slots_obj = accesser.get("slots") + if ctrl_obj is not None and slots_obj is not None: + if isinstance(ctrl_obj, GoDataInteger) and isinstance(slots_obj, GoDataArray): + # Go hardcodes 8 as the number of slots in a bucket. + # The ctrl field is a uint64, and it consists of 8 packed bytes. + if len(slots_obj.contents) == 8: + from_group = [] + # The 0th control byte is stored at the lowest address. + # ctrl.value was read as little-endian, so packing little-endian gets the bytes in memory order. + ctrl_bytes = ctrl_obj.value.to_bytes(8, "little") + for i in range(8): + ctrl = ctrl_bytes[i] + slot_obj = slots_obj.contents[i] + # A slot is full if and only if bit 7 (MSB) is unset in its control byte. + if ctrl & 0x80 == 0: + # Slot is valid. + unpacked = self.__unpack_slot(slot_obj) + if unpacked is None: + from_group = None + break + from_group.append(unpacked) + return from_group + + def __parse_table(self, table_ptr: pointer, nest_depth: int) -> Union[list[tuple[GoData, GoData]], None]: + """ + Given a pointer to a table (an array of groups), parse them all and concatenate results. + + :param int table_ptr: The base address of the table relative to the address space for self.proc. + :param int nest_depth: The further depth allowed when extracting the key and value objects. + :return Union[list[tuple[GoData, GoData]], None]: If parsing succeeded, a list of key/value pairs. Else None. + """ + from_table: Union[list[tuple[GoData, GoData]], None] = None + + groups_reference_base = table_ptr + 8 + self.info.ptr_size + # data is a pointer to an array of groups. + data = safe_read_unsigned(self.info, groups_reference_base, self.info.ptr_size) + length = safe_read_unsigned(self.info, groups_reference_base + self.info.ptr_size, self.info.ptr_size) + + if data is not None and length is not None: + from_table = [] + length += 1 # the stored length is the actual length subtract 1. + group_size = self.group_type.header.size + for i in range(length): + from_group = self.__parse_group(data + i * group_size, nest_depth) + if from_group is None: + from_table = None + break + + from_table.extend(from_group) + + return from_table + + def parse(self, addr: pointer, nest_depth: int) -> GoData: + """ + Extracts data from the header structure of the map. This is the first function to be called to parse a map. + + :param int addr: The memory address (resident in self.proc) to begin unpacking at. + :param int nest_depth: The further depth allowed when extracting the key and value objects. + :return GoData: Returns GoDataMap/GoDataUnparsed/GoDataBad depending on the situation. + """ + extracted: Union[GoData, None] = None + + length = safe_read_unsigned(self.info, addr, 8) + seed = safe_read_unsigned(self.info, addr + 8, self.info.ptr_size) + dir_ptr = safe_read_unsigned(self.info, addr + 8 + self.info.ptr_size, self.info.ptr_size) + dir_len = safe_read_unsigned(self.info, addr + 8 + 2 * self.info.ptr_size, self.info.ptr_size) + if length is not None and seed is not None and dir_ptr is not None and dir_len is not None: + seed_bits = f"{seed:064b}" + if self.info.ptr_size == 4: + seed_bits = seed_bits[32:] + confidence = entropy(seed_bits) ** GO_ENTROPY_SOFTNESS + + if nest_depth > 0: + if length > 0: + + entries: Union[list[tuple[GoData, GoData]], None] = [] + if dir_len == 0: + # Small map type. So dir_ptr points to a group. + entries = self.__parse_group(dir_ptr, nest_depth) + else: + # Regular map type. dir_ptr points to an array of tables. + if dir_len > GO_MAX_SWISSMAP_DIRS: + # This is a very large number of directories: don't parse all of them. + dir_len = GO_MAX_SWISSMAP_DIRS + # We doubt this is actually a map. + confidence = 0.0 + + table_array = safe_read_bytes(self.info, dir_ptr, self.info.ptr_size * dir_len) + if table_array is not None: + for (table_ptr,) in struct.iter_unpack("<" + self.ptr_specifier, table_array): + from_table = self.__parse_table(table_ptr, nest_depth) + if from_table is None: + entries = None + break + # The next line is well-typed because if entries is None, then we already broke. + entries.extend(from_table) # type:ignore[union-attr] + + if entries is not None and len(entries) > 0: + # A valid map. + heuristic_sum = 0.0 + for key, val in entries: + heuristic_sum += key.heuristic + heuristic_sum += val.heuristic + heuristic_avg = heuristic_sum / (2 * len(entries)) + heuristic = (3 * heuristic_avg + confidence) / 4 + extracted = GoDataMap(heuristic=heuristic, entries=entries) + + else: + # Empty map. + extracted = GoDataMap(heuristic=confidence, entries=[]) + else: + # Ran out of depth. + extracted = GoDataUnparsed(heuristic=confidence, address=addr) + + if extracted is None: + extracted = GoDataBad(heuristic=Confidence.JUNK.to_float()) + return extracted diff --git a/common/golang/util.py b/common/golang/util.py new file mode 100644 index 0000000..db9c8a4 --- /dev/null +++ b/common/golang/util.py @@ -0,0 +1,215 @@ +"""Various utility functions used for Go analysis""" + +from functools import lru_cache +from typing import Any, Union + +from lldb import SBError, SBFrame, SBProcess + +from arch.aarch64 import Aarch64 +from arch.arm import Arm +from arch.base_arch import BaseArch +from arch.i386 import I386 +from arch.ppc import PPC +from arch.x86_64 import X86_64 +from common.constants import pointer +from common.golang.interfaces import GoFunc +from common.settings import LLEFSettings +from common.state import LLEFState + + +def go_context_analysis(settings: LLEFSettings) -> bool: + """ + Check preconditions for running Go context analysis functions on the current binary: + 1. The current binary is a Go binary. + 2. The one-shot static Go analysis has been performed successfully. + 3. The user-configurable setting for Go analysis is set to either "auto" or "force". + + :param LLEFSettings settings: The LLEFSettings object for accessing the user-configured Go support level. + :return bool: Returns True if all preconditions for Go context analysis are met, otherwise False. + """ + + return ( + LLEFState.go_state.is_go_binary + and LLEFState.go_state.analysed + and settings.go_support_level in ("auto", "force") + ) + + +def is_address_go_frame_pointer(settings: LLEFSettings, addr: int, frame: SBFrame) -> bool: + """ + Checks whether the given address is the current Go frame pointer. + + :param LLEFSettings settings: The LLEFSettings for checking Go support level. + :param int addr: A stack address. + :param SBFrame frame: The current frame containing PC and SP information. + :return bool: Returns true if the given address is the current Go frame pointer. + """ + if go_context_analysis(settings): + # Need to calculate base pointer as it may not be stored in dedicated bp register depending on arch. + bp = go_calculate_base_pointer(frame.GetPC(), frame.GetSP()) + return addr == bp + + return False + + +def bytes_for_saved_pc(arch: type[BaseArch]) -> int: + """ + Calculates how many bytes are taken up by a saved return address. + + :param Type[BaseArch] arch: The class describing our current target architecture + :return int: The size of a saved return pointer. Returns 0 for unsupported architectures. + """ + if arch in (I386, X86_64): + return arch().bits // 8 + return 0 + + +def go_stackwalk(proc: SBProcess, pc: int, sp: int, bytes_for_pc: int, length: int) -> list[tuple[int, int]]: + """ + Walks back through stack frames from the current frame. Uses metadata intended for Go's panic traceback. + + :param SBProcess proc: The process object currently being debugged. + :param int pc: The current program counter to begin walking at. + :param int sp: The current stack pointer to begin walking at. + :param int bytes_for_pc: How many bytes are taken up by a saved return address. + :return list[tuple[int, int]]: A list of PC, frame pointer pairs tracing through the call stack. + """ + if bytes_for_pc == 0: + # Unsupported architecture for stack walking. + return [] + + out = [] + # Hard-cap the number of iterations, as we only display so many on-screen. + for _ in range(length): + bp = go_calculate_base_pointer(pc, sp) + out.append((pc, bp or 0)) + if bp is None: + break + ra_loc = bp + sp = bp + bytes_for_pc + + err = SBError() + max_pointer_size = 1 << (LLEFState.go_state.pclntab_info.ptr_size * 8) + if ra_loc >= 0 and ra_loc + bytes_for_pc <= max_pointer_size: + pc = proc.ReadUnsignedFromMemory(ra_loc, bytes_for_pc, err) + if err.Fail(): + break + else: + break + return out + + +def go_find_func_name_offset(pc: int) -> tuple[str, int]: + """ + Retrieves the name of the function containing the supplied program counter, and the offset from its entry address. + + :param int pc: Program counter, a pointer to code. + :return tuple[str, int]: Returns the Go function name and offset into it corresponding to the supplied address. + """ + record = go_find_func(pc) + if record is not None: + (entry, gofunc) = record + return (gofunc.name, pc - entry) + + # otherwise, gracefully fail for display purposes + return ("", pc) + + +def pc_binsearch(search_pc: pointer, data: list[tuple[pointer, Any]]) -> Union[tuple[pointer, Any], None]: + """ + Implements a generic binary search to find a record (of any type) + paired to the highest PC that is less than or equal to the search_pc. + + :param pointer search_pc: Program counter, the code address. + :return Union[tuple[pointer, Any], None]: The record associated with the greatest PC still less than search_pc, + otherwise None. + """ + n = len(data) + + # let pcs = map(data, lambda x: x[0]) + # Precondition: pcs is sorted (a safe assumption since the Go runtime relies on it) + # Postcondition: finds i s.t. pcs[i] <= search_pc && pcs[i+1] > search_pc + left = 0 + right = n + # Invariant: pcs[0..left) <= search_pc && pcs[right..n) > search_pc. + # The invariant is true at the beginning and end of every loop cycle. + # Liveness variant: (right - left) is strictly decreasing + while right - left > 0: + middle = (left + right) // 2 + if data[middle][0] <= search_pc: + left = middle + 1 + else: + right = middle + # Inv & !cond => left = right + # => pcs[0..left) <= search_pc && pcs[left..n) > search_pc + + if left == 0: + # all pcs > search_pc + return None + + # func_entries[0..left-1] <= search_pc, so left-1 is our man. + return data[left - 1] + + +# Caching is safe - we clear whenever internal state changes. +@lru_cache(maxsize=128) +def go_find_func(pc: pointer) -> Union[tuple[pointer, GoFunc], None]: + """ + Performs a binary search to find the function record corresponding to a code address. + + :param pointer pc: Program counter, the code address. + :return Union[tuple[pointer, str], None]: Returns the function record containing the program counter, + otherwise None. + """ + result = None + if pc <= LLEFState.go_state.pclntab_info.max_pc_runtime: + func_mapping = LLEFState.go_state.pclntab_info.func_mapping + result = pc_binsearch(pc, func_mapping) + return result + + +# Caching is safe - we clear whenever internal state changes. +@lru_cache(maxsize=128) +def go_calculate_base_pointer(pc: pointer, sp: pointer) -> Union[pointer, None]: + """ + Performs two binary searches to first identify the function, then the stack pointer delta, corresponding to a PC. + + :param pointer pc: The current program counter. + :param pointer sp: The current stack pointer. + :return Union[pointer, None]: Returns the offset from the stack pointer to the Go frame pointer, + else None if unknown. + """ + if pc <= LLEFState.go_state.pclntab_info.max_pc_runtime: + func_mapping = LLEFState.go_state.pclntab_info.func_mapping + + result = pc_binsearch(pc, func_mapping) + if result is not None: + stack_deltas = result[1].stack_deltas + + result2 = pc_binsearch(pc, stack_deltas) + if result2 is not None: + stack_delta: int = result2[1] + return sp + stack_delta + return None + + +def get_arg_registers(arch: BaseArch) -> list[str]: + """ + Get a sequence of register names in which Go will pass arguments before going to the stack. + See https://go.dev/s/regabi. + + :param BaseArch arch: The object describing our current target architecture + :return list[str]: The ordered list of register names that Go passes function arguments in. + """ + if isinstance(arch, I386): + return ["eax", "ebx", "ecx", "edi", "esi"] + elif isinstance(arch, X86_64): + return ["rax", "rbx", "rcx", "rdi", "rsi", "r8", "r9", "r10", "r11"] + elif isinstance(arch, Arm): + return ["r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7"] + elif isinstance(arch, Aarch64): + return ["x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15"] + elif isinstance(arch, PPC): + return ["r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10"] + else: + return [] diff --git a/common/golang/util_stateless.py b/common/golang/util_stateless.py new file mode 100644 index 0000000..e5f02a9 --- /dev/null +++ b/common/golang/util_stateless.py @@ -0,0 +1,207 @@ +"""Various utility functions used for Go analysis that don't require importing LLEFState. +Mainly used for avoiding circular imports.""" + +import itertools +import math +from typing import Any, Iterator, Union + +from lldb import UINT32_MAX, SBTarget + +from common.constants import pointer + + +def file_to_load_address(target: SBTarget, static_address: int) -> pointer: + """ + Converts an in-file address into an in-memory address. + + :param SBTarget target: The target associated with the current process. + :param int static_address: The address as described in the binary. + :return int: The corresponding address as it has been mapped in memory. + """ + return target.ResolveFileAddress(static_address).GetLoadAddress(target) + + +def read_varint(bytebuf: Any, offset: int) -> tuple[int, int]: + """ + Reads a variable-length unsigned integer (varint) as per Go's encoding. + + :param Any bytebuf: An array-like object supporting the extraction of bytes by indexing. + :param int offset: The offset to start reading the variable-length integer at. + :return tuple[int, int]: A pair of the decoded value and the first unread offset (for chaining calls). + """ + value = 0 + shift = 0 + + # The number is split into 7-bit groups and encoded with the least significant group first. + # The 8th bit tells us whether another group is coming. + # The number is never more than 32 bits long, so 5 iterations is enough. + # https://docs.google.com/document/d/1lyPIbmsYbXnpNj57a261hgOYVpNRcgydurVQIyZOz_o/pub + for _ in range(5): + b = bytebuf[offset] + offset += 1 + + # Mask off the continuation-indicating bit. + value |= (b & 0b01111111) << shift + + if b & 0b10000000 == 0: + break + shift += 7 + return value & UINT32_MAX, offset + + +def entropy(bitstring: str) -> float: + """ + Calculates the entropy of a short string consisting only of 0s and 1s. The string should be no more than + 1000 characters long, otherwise overly-large numbers will be generated during the calculation. + The method examines the number of bit flips while reading left to right. + + :param str bitstring: A string over '0' and '1' of length up to 1000. + :return float: The probability of seeing the number of bit flips (or a more unlikely result) + if the input were drawn from a unifom random distribution. + """ + n = len(bitstring) - 1 + bit_changes = 0 + for i in range(1, n + 1): + if bitstring[i - 1] != bitstring[i]: + bit_changes += 1 + + if bit_changes > n // 2: + bit_changes = n - bit_changes + coeffs = 0.0 + power = 0.5 ** (n - 1) + for x in range(bit_changes + 1): + coeffs += math.comb(n, x) + return coeffs * power + + +def rate_candidate_length(length: int, threshold: float, softness: float) -> float: + """ + Dynamic datatypes that encode their length as a field are normally not too long. If the decoded length is extremely + large, then we probably mistook this memory for a type other than the one it actually is. This function grades the + length of a slice or string, with an ultimately inversely proportional relationship between length and return value. + + :param int length: The candidate length of this datatype. + :param float threshold: The maximum length that can be awarded a score of 1.0. + :param float softness: The resistance of the returned score to decrease - higher means a longer tail in the curve. + :return float: A float between 0.0 and 1.0. + """ + + k = threshold * softness + return min(1.0, k / (length + k - threshold)) + + +class LeastRecentlyAddedDictionary: + """ + A form of least-recently-added mapping datastructure. + The first entry to be evicted is the one that was added/modified last. + The keys are integers in this implementation. + """ + + length: int + capacity: int + addition_uid: Iterator[int] + + # (key, addition_id, value). None means empty slot. + store: list[Union[tuple[int, int, Any], None]] + + def __init__(self, capacity: int = 128): + self.capacity = capacity + self.store = [] + for _ in range(capacity): + self.store.append(None) + self.length = 0 + self.addition_uid = itertools.count() + + def __get_idx(self, key: int) -> Union[int, None]: + """ + Internal linear search for a record with matching key. + + :param int key: The search key. + :return Union[int, None]: If found, the index in the store. Otherwise None. + """ + for i in range(self.capacity): + record = self.store[i] + if record is not None and record[0] == key: + return i + return None + + def __get_lra(self) -> int: + """ + Precondition: self.length > 0. + Finds the least-recently-added entry in the store. + + :return int: The index of the least-recently-added entry. + """ + # Precondition: self.length > 0 + lowest_uid: Union[int, None] = None + lowest_uid_index = None + for i in range(self.capacity): + record = self.store[i] + if record is not None and (lowest_uid is None or record[1] < lowest_uid): + lowest_uid = record[1] + lowest_uid_index = i + + # lowest_uid_index is always not None by precondition. + if lowest_uid_index is not None: + return lowest_uid_index + else: + return 0 + + def add(self, key: int, val: Any) -> None: + """ + Equivalent to Python's dict[key] = val. Will silently overwrite a LRA entry if out of room. + + :param int key: The key to add the value against. + :param Any val: The value to add against the key. + """ + uid = next(self.addition_uid) + record = (key, uid, val) + + if self.length == 0: + # Empty dict + self.store[0] = record + self.length += 1 + else: + # Other entries in dict + index = self.__get_idx(key) + if index is not None: + # Already present: overwrite but bump the added time + self.store[index] = record + else: + # Not already present. + if self.length < self.capacity: + # There's a free space somewhere + spot = self.store.index(None) + self.store[spot] = record + self.length += 1 + else: + # At capacity: evict the LRA. + lra_index = self.__get_lra() + self.store[lra_index] = record + + def delete(self, key: int) -> None: + """ + Equivalent to Python's del dict[key]. Fails silently if key not in dict. + + :param int key: The key to delete. + """ + position = self.__get_idx(key) + if position is not None: + self.store[position] = None + self.length -= 1 + + def search(self, key: int, default: Any = None) -> Any: + """ + Equivalent to Python's dict.get(key, default=...). + Will try to lookup the key, falling back to default if not present. + + :param int key: The key to search for. + :param Any default: The value to return if the key could not be found, defaults to None + :return Any: The value next to the given key, otherwise the default value if it could not be found. + """ + position = self.__get_idx(key) + if position is not None: + # Next line is well-typed because self.__get_idx(key) ensures self.store[position] is not None. + return self.store[position][2] # type: ignore[index] + else: + return default diff --git a/common/instruction_util.py b/common/instruction_util.py index 8928284..2404707 100644 --- a/common/instruction_util.py +++ b/common/instruction_util.py @@ -1,15 +1,18 @@ import re -from typing import List +from re import Match from lldb import SBAddress, SBInstruction, SBTarget from common.color_settings import LLEFColorSettings +from common.golang.analysis import go_annotate_jumps +from common.golang.util import go_context_analysis from common.output_util import color_string, output_line +from common.settings import LLEFSettings def extract_instructions( target: SBTarget, start_address: int, end_address: int, disassembly_flavour: str -) -> List[SBInstruction]: +) -> list[SBInstruction]: """ Returns a list of instructions between a range of memory address defined by @start_address and @end_address. @@ -36,7 +39,7 @@ def extract_instructions( def color_operands( operands: str, color_settings: LLEFColorSettings, -): +) -> str: """ Colors the registers and addresses in the instruction's operands. @@ -52,10 +55,10 @@ def color_operands( # A register can NEVER start with numbers or any other special character other than '%'. register_pattern = r"(? str: return color_string(match.group(0), color_settings.register_color) - def color_address(match): + def color_address(match: Match[str]) -> str: return color_string(match.group(0), color_settings.address_operand_color) operands = re.sub(register_pattern, color_register, operands) @@ -67,7 +70,9 @@ def color_address(match): def print_instruction( target: SBTarget, instruction: SBInstruction, - base: int, + lldb_frame_start: int, + function_start: int, + settings: LLEFSettings, color_settings: LLEFColorSettings, highlight: bool = False, ) -> None: @@ -82,7 +87,7 @@ def print_instruction( """ address = instruction.GetAddress().GetLoadAddress(target) - offset = address - base + offset = address - function_start line = hex(address) if offset >= 0: @@ -94,12 +99,16 @@ def print_instruction( operands = instruction.GetOperands(target) or "" comment = instruction.GetComment(target) or "" + ops_width = len(operands) # visible length, for spacing (before colouring) + if go_context_analysis(settings): + comment = go_annotate_jumps(target, instruction, lldb_frame_start, comment) + if not highlight: operands = color_operands(operands, color_settings) if comment != "": comment = f"; {comment}" - line += f"{mnemonic:<10}{operands:<30}{comment}" + line += f"{mnemonic:<10}{operands.ljust(35 + len(operands) - ops_width)}{comment}" if highlight: line = color_string(line, color_settings.highlighted_instruction_color) @@ -109,8 +118,10 @@ def print_instruction( def print_instructions( target: SBTarget, - instructions: List[SBInstruction], - base: int, + instructions: list[SBInstruction], + lldb_frame_start: int, + function_start: int, + settings: LLEFSettings, color_settings: LLEFColorSettings, ) -> None: """ @@ -122,4 +133,4 @@ def print_instructions( :param color_settings: Contains the color settings to color the instruction. """ for instruction in instructions: - print_instruction(target, instruction, base, color_settings) + print_instruction(target, instruction, lldb_frame_start, function_start, settings, color_settings) diff --git a/common/output_util.py b/common/output_util.py index f3a958d..7d6f251 100644 --- a/common/output_util.py +++ b/common/output_util.py @@ -1,15 +1,29 @@ """Utility functions related to terminal output.""" +import os import re import shutil from textwrap import TextWrapper -from typing import Any +from typing import Any, Union + +from lldb import SBAddress from common.constants import ALIGN, DEFAULT_TERMINAL_COLUMNS, DEFAULT_TERMINAL_LINES, GLYPHS, MSG_TYPE, TERM_COLORS from common.state import LLEFState -def color_string(string: str, color_setting: str, lwrap: str = "", rwrap: str = "") -> str: +def generate_rebased_address_string(address: SBAddress, rebase_addresses: bool, rebase_offset: int, col: str) -> str: + module = address.GetModule() + + if module is not None and rebase_addresses is True: + file_name = os.path.basename(str(module.file)) + rebased_address = address.GetFileAddress() + rebase_offset + return color_string(f"({file_name} {rebased_address:#x})", col) + + return "" + + +def color_string(string: str, color_setting: Union[str, None], lwrap: str = "", rwrap: str = "") -> str: """ Colors a @string based on the @color_setting. Optional: Wrap the string with uncolored strings @lwrap and @rwrap. diff --git a/common/settings.py b/common/settings.py index a5e5669..3e4765a 100644 --- a/common/settings.py +++ b/common/settings.py @@ -1,6 +1,7 @@ """Global settings module""" import os +from typing import Union from lldb import SBDebugger @@ -19,73 +20,95 @@ class LLEFSettings(BaseLLEFSettings, metaclass=Singleton): LLEF_CONFIG_PATH = os.path.join(os.path.expanduser("~"), ".llef") GLOBAL_SECTION = "LLEF" DEFAUL_OUTPUT_ORDER = "registers,stack,code,threads,trace" - debugger: SBDebugger = None + debugger: Union[SBDebugger, None] = None @property - def color_output(self): + def color_output(self) -> bool: default = False if self.debugger is not None: default = self.debugger.GetUseColor() return self._RAW_CONFIG.getboolean(self.GLOBAL_SECTION, "color_output", fallback=default) @property - def register_coloring(self): + def register_coloring(self) -> bool: return self._RAW_CONFIG.getboolean(self.GLOBAL_SECTION, "register_coloring", fallback=True) @property - def show_legend(self): + def show_legend(self) -> bool: return self._RAW_CONFIG.getboolean(self.GLOBAL_SECTION, "show_legend", fallback=True) @property - def show_registers(self): + def show_registers(self) -> bool: return self._RAW_CONFIG.getboolean(self.GLOBAL_SECTION, "show_registers", fallback=True) @property - def show_stack(self): + def show_stack(self) -> bool: return self._RAW_CONFIG.getboolean(self.GLOBAL_SECTION, "show_stack", fallback=True) @property - def show_code(self): + def show_code(self) -> bool: return self._RAW_CONFIG.getboolean(self.GLOBAL_SECTION, "show_code", fallback=True) @property - def show_threads(self): + def show_threads(self) -> bool: return self._RAW_CONFIG.getboolean(self.GLOBAL_SECTION, "show_threads", fallback=True) @property - def show_trace(self): + def show_trace(self) -> bool: return self._RAW_CONFIG.getboolean(self.GLOBAL_SECTION, "show_trace", fallback=True) @property - def force_arch(self): + def force_arch(self) -> Union[str, None]: arch = self._RAW_CONFIG.get(self.GLOBAL_SECTION, "force_arch", fallback=None) return None if arch not in supported_arch else arch @property - def rebase_addresses(self): + def rebase_addresses(self) -> bool: return self._RAW_CONFIG.getboolean(self.GLOBAL_SECTION, "rebase_addresses", fallback=True) @property - def rebase_offset(self): + def rebase_offset(self) -> int: return self._RAW_CONFIG.getint(self.GLOBAL_SECTION, "rebase_offset", fallback=0x100000) @property - def show_all_registers(self): + def show_all_registers(self) -> bool: return self._RAW_CONFIG.getboolean(self.GLOBAL_SECTION, "show_all_registers", fallback=False) @property - def output_order(self): + def output_order(self) -> str: return self._RAW_CONFIG.get(self.GLOBAL_SECTION, "output_order", fallback=self.DEFAUL_OUTPUT_ORDER) @property - def truncate_output(self): + def truncate_output(self) -> bool: return self._RAW_CONFIG.getboolean(self.GLOBAL_SECTION, "truncate_output", fallback=True) @property - def enable_darwin_heap_scan(self): + def enable_darwin_heap_scan(self) -> bool: return self._RAW_CONFIG.getboolean(self.GLOBAL_SECTION, "enable_darwin_heap_scan", fallback=False) - def validate_output_order(self, value: str): + @property + def go_support_level(self) -> str: + support_level = self._RAW_CONFIG.get(self.GLOBAL_SECTION, "go_support_level", fallback="auto").lower() + return "auto" if support_level not in ("disable", "auto", "force") else support_level + + @property + def max_trace_length(self) -> int: + return self._RAW_CONFIG.getint(self.GLOBAL_SECTION, "max_trace_length", fallback=10) + + @property + def stack_view_size(self) -> int: + return self._RAW_CONFIG.getint(self.GLOBAL_SECTION, "stack_view_size", fallback=12) + + @property + def max_disassembly_length(self) -> int: + return self._RAW_CONFIG.getint(self.GLOBAL_SECTION, "max_disassembly_length", fallback=9) + + @property + def go_confidence_threshold(self) -> str: + threshold = self._RAW_CONFIG.get(self.GLOBAL_SECTION, "go_confidence_threshold", fallback="low").lower() + return "low" if threshold not in ("low", "medium", "high") else threshold + + def validate_output_order(self, value: str) -> None: default_sections = self.DEFAUL_OUTPUT_ORDER.split(",") sections = value.split(",") if len(sections) != len(default_sections): @@ -99,7 +122,7 @@ def validate_output_order(self, value: str): if len(missing_sections) > 0: raise ValueError(f"Missing '{','.join(missing_sections)}' from output order.") - def validate_settings(self, setting=None) -> bool: + def validate_settings(self, setting: str = "") -> bool: """ Validate settings by attempting to retrieve all properties thus executing any ConfigParser coverters """ @@ -134,7 +157,7 @@ def __init__(self, debugger: SBDebugger): super().__init__() self.debugger = debugger - def set(self, setting: str, value: str): + def set(self, setting: str, value: str) -> None: super().set(setting, value) if setting == "color_output": @@ -142,7 +165,7 @@ def set(self, setting: str, value: str): elif setting == "truncate_output": self.state.change_truncate_output(self.truncate_output) - def load(self, reset=False): + def load(self, reset: bool = False) -> None: super().load(reset) self.state.change_use_color(self.color_output) self.state.change_truncate_output(self.truncate_output) diff --git a/common/singleton.py b/common/singleton.py index 947a249..b36614f 100644 --- a/common/singleton.py +++ b/common/singleton.py @@ -1,14 +1,16 @@ """Singleton module""" +from typing import Any + class Singleton(type): """ Singleton class implementation. Use with metaclass=Singleton. """ - _instances = {} + _instances: dict[type, Any] = {} - def __call__(cls, *args, **kwargs): + def __call__(cls, *args: Any, **kwargs: Any) -> Any: if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] diff --git a/common/state.py b/common/state.py index 0a742ff..ec04e93 100644 --- a/common/state.py +++ b/common/state.py @@ -1,7 +1,8 @@ """Global state module""" -from typing import Dict +from typing import Any +from common.golang.state import GoState from common.singleton import Singleton @@ -11,13 +12,13 @@ class LLEFState(metaclass=Singleton): """ # Stores previous register state at the last breakpoint - prev_registers: Dict[str, int] = {} + prev_registers: dict[str, int] = {} # Stores register state at the current breakpoint (caches the contents of the current frame as frame is mutable) - current_registers: Dict[str, int] = {} + current_registers: dict[str, int] = {} # Stores patterns created by the `pattern` command - created_patterns = [] + created_patterns: list[dict[str, Any]] = [] # Stores whether color should be used use_color = False @@ -26,13 +27,16 @@ class LLEFState(metaclass=Singleton): truncate_output = True # Stores version of LLDB if on Linux. Stores clang verion if on Mac - version = [] + version: list[int] = [] # Linux, Mac (Darwin) or Windows platform = "" disassembly_syntax = "" + # Stores Go-specific analysis. None means we are yet to analyse. + go_state = GoState() + def change_use_color(self, new_value: bool) -> None: """ Change the global use_color bool. use_color should not be written to directly diff --git a/common/util.py b/common/util.py index 4325f72..b5e5538 100644 --- a/common/util.py +++ b/common/util.py @@ -3,7 +3,8 @@ import os import shutil from argparse import ArgumentTypeError -from typing import List, Tuple +from collections.abc import Callable +from typing import Any, Union from lldb import ( SBAddress, @@ -22,7 +23,9 @@ ) from common.constants import DEFAULT_TERMINAL_COLUMNS, MAGIC_BYTES, MSG_TYPE, TERM_COLORS +from common.golang.util import go_context_analysis, go_find_func_name_offset from common.output_util import print_message +from common.settings import LLEFSettings from common.state import LLEFState @@ -43,10 +46,13 @@ def address_to_filename(target: SBTarget, address: int) -> str: file_spec = module.GetSymbolFileSpec() filename = file_spec.GetFilename() + if filename is None: + filename = module.GetFileSpec().GetFilename() + return filename -def get_frame_range(frame: SBFrame, target: SBTarget) -> Tuple[str, str]: +def get_frame_range(frame: SBFrame, target: SBTarget) -> tuple[int, int]: function = frame.GetFunction() if function: start_address = function.GetStartAddress().GetLoadAddress(target) @@ -58,18 +64,49 @@ def get_frame_range(frame: SBFrame, target: SBTarget) -> Tuple[str, str]: return start_address, end_address -def get_registers(frame: SBFrame, frame_type: str) -> List[SBValue]: +def get_registers(frame: SBFrame, frame_type: str) -> Union[SBValue, list[SBValue]]: """ Returns the registers in @frame that are of the specified @type. A @type is a string defined in LLDB, e.g. "General Purpose" """ - registers = [] + registers: Union[SBValue, list[SBValue]] = [] for regs in frame.GetRegisters(): - if frame_type.lower() in regs.GetName().lower(): - registers = regs + regs_name = regs.GetName() + if regs_name is not None: + if frame_type.lower() in regs_name.lower(): + registers = regs return registers +def get_function_info_from_frame(settings: LLEFSettings, target: SBTarget, frame: SBFrame) -> tuple[str, int]: + """ + Attempt to find a function name and offset that the PC is within the function, given the current debug frame. + + :param LLEFSettings settings: The LLEFSettings object for determining the Go support level. + :param SBTarget target: The target associated with the current process. For converting file->load addresses. + :param SBFrame frame: The debugger frame. + :return tuple[str, int]: A tuple of (function name, offset). + """ + pc = frame.GetPC() + function_name = "" + + if go_context_analysis(settings): + function_name, offset = go_find_func_name_offset(pc) + + # Use LLDB API to get function name and offset. + if function_name == "": + function = frame.GetFunction() + if function: + function_name = function.GetName() + offset = pc - function.GetStartAddress().GetLoadAddress(target) + else: + symbol = frame.GetSymbol() + function_name = symbol.GetName() + offset = pc - symbol.GetStartAddress().GetLoadAddress(target) + + return (function_name, offset) + + def get_frame_arguments(frame: SBFrame, frame_argument_name_color: TERM_COLORS) -> str: """ Returns a string containing args of the supplied frame @@ -79,20 +116,20 @@ def get_frame_arguments(frame: SBFrame, frame_argument_name_color: TERM_COLORS) args = [] for var in variables: # get and format argument value - value = "???" + val = "???" var_value = var.GetValue() if var_value is None: - value = "null" + val = "null" elif var_value: try: - value = f"{int(var.GetValue(), 0):#x}" + val = f"{int(var.GetValue(), 0):#x}" except ValueError: pass - args.append(f"{frame_argument_name_color.value}{var.GetName()}{TERM_COLORS.ENDC.value}={value}") + args.append(f"{frame_argument_name_color.value}{var.GetName()}{TERM_COLORS.ENDC.value}={val}") return f"({' '.join(args)})" -def attempt_to_read_string_from_memory(process: SBProcess, addr: SBValue, buffer_size: int = 256) -> str: +def attempt_to_read_string_from_memory(process: SBProcess, addr: int, buffer_size: int = 256) -> str: """ Returns a string from a memory address if one can be read, else an empty string """ @@ -108,7 +145,7 @@ def attempt_to_read_string_from_memory(process: SBProcess, addr: SBValue, buffer return ret_string -def is_ascii_string(address: SBValue, process: SBProcess) -> bool: +def is_ascii_string(address: int, process: SBProcess) -> bool: """ Determines if a given memory @address contains a readable string. @@ -119,7 +156,7 @@ def is_ascii_string(address: SBValue, process: SBProcess) -> bool: return attempt_to_read_string_from_memory(process, address) != "" -def is_in_section(address: SBValue, target: SBTarget, target_section_name: str) -> bool: +def is_in_section(address: int, target: SBTarget, target_section_name: str) -> bool: """ Determines whether a given memory @address exists within a @section of the executable file @target. @@ -141,7 +178,7 @@ def is_in_section(address: SBValue, target: SBTarget, target_section_name: str) return target_section_name in full_section_name -def is_text_region(address: SBValue, target: SBTarget, region: SBMemoryRegionInfo) -> bool: +def is_text_region(address: int, target: SBTarget, region: SBMemoryRegionInfo) -> bool: """ Determines if a given memory @address if within a '.text' section of the target executable. @@ -165,7 +202,7 @@ def is_text_region(address: SBValue, target: SBTarget, region: SBMemoryRegionInf return in_text -def is_code(address: SBValue, process: SBProcess, target: SBTarget, regions: SBMemoryRegionInfoList) -> bool: +def is_code(address: int, process: SBProcess, target: SBTarget, regions: Union[SBMemoryRegionInfoList, None]) -> bool: """Determines whether an @address points to code""" region = SBMemoryRegionInfo() code_bool = False @@ -176,7 +213,9 @@ def is_code(address: SBValue, process: SBProcess, target: SBTarget, regions: SBM return code_bool -def is_stack(address: SBValue, regions: SBMemoryRegionInfoList, darwin_stack_regions: List[SBMemoryRegionInfo]) -> bool: +def is_stack( + address: int, regions: Union[SBMemoryRegionInfoList, None], darwin_stack_regions: list[SBMemoryRegionInfo] +) -> bool: """Determines whether an @address points to the stack""" stack_bool = False @@ -191,11 +230,11 @@ def is_stack(address: SBValue, regions: SBMemoryRegionInfoList, darwin_stack_reg def is_heap( - address: SBValue, + address: int, target: SBTarget, - regions: SBMemoryRegionInfoList, - stack_regions: List[SBMemoryRegionInfo], - darwin_heap_regions: List[Tuple[int, int]], + regions: Union[SBMemoryRegionInfoList, None], + stack_regions: list[SBMemoryRegionInfo], + darwin_heap_regions: Union[list[tuple[int, int]], None], ) -> bool: """Determines whether an @address points to the heap""" heap_bool = False @@ -218,12 +257,7 @@ def is_heap( return heap_bool -def extract_arch_from_triple(triple: str) -> str: - """Extracts the architecture from triple string.""" - return triple.split("-")[0] - - -def verify_version(version: List[int], target_version: List[int]) -> bool: +def verify_version(version: list[int], target_version: list[int]) -> bool: """Checks if the @version is greater than or equal to the @target_version.""" length_difference = len(target_version) - len(version) if length_difference > 0: @@ -234,7 +268,7 @@ def verify_version(version: List[int], target_version: List[int]) -> bool: return version >= target_version -def lldb_version_to_clang(lldb_version): +def lldb_version_to_clang(lldb_version: list[int]) -> list[int]: """ Convert an LLDB version to its corresponding Clang version. @@ -253,7 +287,7 @@ def lldb_version_to_clang(lldb_version): return clang_version -def check_version(required_version_string): +def check_version(required_version_string: str): def inner(func): def wrapper(*args, **kwargs): required_version = [int(x) for x in required_version_string.split(".")] @@ -269,7 +303,7 @@ def wrapper(*args, **kwargs): return inner -def check_process(func): +def check_process(func: Callable[..., Any]) -> Callable[..., Any]: """ Checks that there's a running process before executing the wrapped function. Only to be used on overrides of `__call__`. @@ -277,7 +311,7 @@ def check_process(func): :param func: Wrapped function to be executed after successful check. """ - def wrapper(*args, **kwargs): + def wrapper(*args: Any, **kwargs: Any) -> Any: for arg in list(args) + list(kwargs.values()): if isinstance(arg, SBExecutionContext): if arg.process.is_alive: @@ -291,8 +325,8 @@ def wrapper(*args, **kwargs): return wrapper -def check_target(func): - def wrapper(*args, **kwargs): +def check_target(func: Callable[..., Any]) -> Callable[..., Any]: + def wrapper(*args: Any, **kwargs: Any) -> Any: for arg in list(args) + list(kwargs.values()): if isinstance(arg, SBExecutionContext): if arg.target.IsValid(): @@ -306,14 +340,14 @@ def wrapper(*args, **kwargs): return wrapper -def is_file(target: SBTarget, expected_magic_bytes: List[bytes]): +def is_file(target: SBTarget, expected_magic_bytes: list[bytes]) -> bool: """Read signature of @target file and compare to expected magic bytes.""" magic_bytes = read_program(target, 0, 4) return magic_bytes in expected_magic_bytes -def check_elf(func): - def wrapper(*args, **kwargs): +def check_elf(func: Callable[..., Any]) -> Callable[..., Any]: + def wrapper(*args: Any, **kwargs: Any) -> Any: for arg in list(args) + list(kwargs.values()): if isinstance(arg, SBExecutionContext): try: @@ -331,7 +365,7 @@ def wrapper(*args, **kwargs): return wrapper -def hex_int(x): +def hex_int(x: str) -> int: """A converter for input arguments in different bases to ints. For base 0, the base is determined by the prefix. So, numbers starting `0x` are hex and numbers with no prefix are decimal. Base 0 also disallows leading zeros. @@ -339,15 +373,15 @@ def hex_int(x): return int(x, 0) -def positive_int(x): +def positive_int(x: str) -> int: """A converter for input arguments in different bases to positive ints""" - x = hex_int(x) - if x <= 0: + int_x = hex_int(x) + if int_x <= 0: raise ArgumentTypeError("Must be positive") - return x + return int_x -def hex_or_str(x): +def hex_or_str(x: Union[str, int]) -> str: """Convert to formatted hex if an integer, otherwise return the value.""" if isinstance(x, int): return f"0x{x:016x}" @@ -355,7 +389,7 @@ def hex_or_str(x): return x -def read_program(target: SBTarget, offset: int, n: int): +def read_program(target: SBTarget, offset: int, n: int) -> bytes: """ Read @n bytes from a given @offset from the start of @target object file. @@ -372,13 +406,13 @@ def read_program(target: SBTarget, offset: int, n: int): address.OffsetAddress(offset) data = target.ReadMemory(address, n, error) - if error.Fail(): + if error.Fail() or data is None: raise MemoryError(f"Couldn't read memory at file offset {hex(address.GetOffset())}.") return data -def read_program_int(target: SBTarget, offset: int, n: int): +def read_program_int(target: SBTarget, offset: int, n: int) -> int: """ Read @n bytes from a given @offset from the start of @target object file, and convert to integer by little endian. @@ -393,7 +427,7 @@ def read_program_int(target: SBTarget, offset: int, n: int): return int.from_bytes(data, "little") -def find_stack_regions(process: SBProcess) -> List[SBMemoryRegionInfo]: +def find_stack_regions(process: SBProcess) -> list[SBMemoryRegionInfo]: """ Find all memory regions containing the stack by looping through stack pointers in each frame. @@ -409,11 +443,11 @@ def find_stack_regions(process: SBProcess) -> List[SBMemoryRegionInfo]: return stack_regions -def find_darwin_heap_regions(process: SBProcess) -> List[Tuple[int, int]]: +def find_darwin_heap_regions(process: SBProcess) -> Union[list[tuple[int, int]], None]: """ Find memory heap regions on Darwin. - :return: List[Tuple[int, int]]: A list containing values for min and max ranges for heap regions on Darwin. + :return: list[tuple[int, int]]: A list containing values for min and max ranges for heap regions on Darwin. """ MAX_MATCHES = 128 @@ -453,6 +487,6 @@ def find_darwin_heap_regions(process: SBProcess) -> List[Tuple[int, int]]: heap_regions.append((lo_addr, hi_addr)) else: # Fallback to default way to calculate heap regions in error condition. - heap_regions = None + return None return heap_regions diff --git a/handlers/stop_hook.py b/handlers/stop_hook.py index 2ff8aca..717fe13 100644 --- a/handlers/stop_hook.py +++ b/handlers/stop_hook.py @@ -1,6 +1,6 @@ """Break point handler.""" -from typing import Any, Dict +from typing import Any from lldb import SBDebugger, SBExecutionContext, SBStream, SBStructuredData, SBTarget @@ -19,7 +19,7 @@ def lldb_self_register(cls, debugger: SBDebugger, module_name: str) -> None: command = f"target stop-hook add -P {module_name}.{cls.__name__}" debugger.HandleCommand(command) - def __init__(self, target: SBTarget, _: SBStructuredData, __: Dict[Any, Any]) -> None: + def __init__(self, target: SBTarget, _: SBStructuredData, __: dict[Any, Any]) -> None: """ For up to date documentation on args provided to this function run: `help target stop-hook add` """ diff --git a/llef.py b/llef.py index 7f3adf4..34e32d1 100644 --- a/llef.py +++ b/llef.py @@ -11,7 +11,7 @@ # --------------------------------------------------------------------- import platform -from typing import Any, Dict, List, Type, Union +from typing import Any, Union from lldb import SBDebugger @@ -21,6 +21,14 @@ from commands.color_settings import ColorSettingsCommand from commands.context import ContextCommand from commands.dereference import DereferenceCommand +from commands.golang import ( + GolangBacktraceCommand, + GolangContainer, + GolangFindFuncCommand, + GolangGetTypeCommand, + GolangReanalyseCommand, + GolangUnpackTypeCommand, +) from commands.hexdump import HexdumpCommand from commands.pattern import PatternContainer, PatternCreateCommand, PatternSearchCommand from commands.scan import ScanCommand @@ -30,8 +38,8 @@ from handlers.stop_hook import StopHookHandler -def __lldb_init_module(debugger: SBDebugger, _: Dict[Any, Any]) -> None: - commands: List[Union[Type[BaseCommand], Type[BaseContainer]]] = [ +def __lldb_init_module(debugger: SBDebugger, _: dict[Any, Any]) -> None: + commands: list[Union[type[BaseCommand], type[BaseContainer]]] = [ PatternContainer, PatternCreateCommand, PatternSearchCommand, @@ -43,6 +51,12 @@ def __lldb_init_module(debugger: SBDebugger, _: Dict[Any, Any]) -> None: XinfoCommand, DereferenceCommand, ScanCommand, + GolangContainer, + GolangBacktraceCommand, + GolangFindFuncCommand, + GolangGetTypeCommand, + GolangUnpackTypeCommand, + GolangReanalyseCommand, ] handlers = [StopHookHandler] diff --git a/typings/lldb/__init__.pyi b/typings/lldb/__init__.pyi new file mode 100644 index 0000000..dc319df --- /dev/null +++ b/typings/lldb/__init__.pyi @@ -0,0 +1,4063 @@ +from collections.abc import Callable, Iterator +from io import IOBase +from typing import Any, Union +from uuid import UUID + +# Incomplete is a synonym for Any +from _typeshed import Incomplete + +class _SwigNonDynamicMeta(type): + __setattr__: Incomplete + +swig_version: Incomplete + +def lldb_iter(obj: Incomplete, getsize: Incomplete, getelem: Incomplete) -> Iterator[Incomplete]: ... + +INT32_MAX: int +UINT32_MAX: int +UINT64_MAX: int +LLDB_GENERIC_ERROR: int +LLDB_INVALID_BREAK_ID: int +LLDB_DEFAULT_BREAK_SIZE: int +LLDB_INVALID_WATCH_ID: int +LLDB_WATCH_TYPE_READ: int +LLDB_WATCH_TYPE_WRITE: int +LLDB_WATCH_TYPE_MODIFY: int +LLDB_INVALID_SITE_ID: int +LLDB_REGNUM_GENERIC_PC: int +LLDB_REGNUM_GENERIC_SP: int +LLDB_REGNUM_GENERIC_FP: int +LLDB_REGNUM_GENERIC_RA: int +LLDB_REGNUM_GENERIC_FLAGS: int +LLDB_REGNUM_GENERIC_ARG1: int +LLDB_REGNUM_GENERIC_ARG2: int +LLDB_REGNUM_GENERIC_ARG3: int +LLDB_REGNUM_GENERIC_ARG4: int +LLDB_REGNUM_GENERIC_ARG5: int +LLDB_REGNUM_GENERIC_ARG6: int +LLDB_REGNUM_GENERIC_ARG7: int +LLDB_REGNUM_GENERIC_ARG8: int +LLDB_REGNUM_GENERIC_TP: int +LLDB_INVALID_STOP_ID: int +LLDB_INVALID_ADDRESS: int +LLDB_INVALID_INDEX32: int +LLDB_INVALID_IVAR_OFFSET: int +LLDB_INVALID_IMAGE_TOKEN: int +LLDB_INVALID_MODULE_VERSION: int +LLDB_INVALID_REGNUM: int +LLDB_INVALID_UID: int +LLDB_INVALID_PROCESS_ID: int +LLDB_INVALID_THREAD_ID: int +LLDB_INVALID_FRAME_ID: int +LLDB_INVALID_SIGNAL_NUMBER: int +LLDB_INVALID_OFFSET: int +LLDB_INVALID_LINE_NUMBER: int +LLDB_INVALID_COLUMN_NUMBER: int +LLDB_INVALID_QUEUE_ID: int +LLDB_INVALID_CPU_ID: int +LLDB_INVALID_WATCHPOINT_RESOURCE_ID: int +LLDB_ARCH_DEFAULT: str +LLDB_ARCH_DEFAULT_32BIT: str +LLDB_ARCH_DEFAULT_64BIT: str +LLDB_INVALID_CPUTYPE: int +LLDB_MAX_NUM_OPTION_SETS: int +LLDB_OPT_SET_ALL: int +LLDB_OPT_SET_1: int +LLDB_OPT_SET_2: int +LLDB_OPT_SET_3: int +LLDB_OPT_SET_4: int +LLDB_OPT_SET_5: int +LLDB_OPT_SET_6: int +LLDB_OPT_SET_7: int +LLDB_OPT_SET_8: int +LLDB_OPT_SET_9: int +LLDB_OPT_SET_10: int +LLDB_OPT_SET_11: int +LLDB_OPT_SET_12: int +LLDB_INVALID_ADDRESS_MASK: int +eStateInvalid: int +eStateUnloaded: int +eStateConnected: int +eStateAttaching: int +eStateLaunching: int +eStateStopped: int +eStateRunning: int +eStateStepping: int +eStateCrashed: int +eStateDetached: int +eStateExited: int +eStateSuspended: int +kLastStateType: int +eLaunchFlagNone: int +eLaunchFlagExec: int +eLaunchFlagDebug: int +eLaunchFlagStopAtEntry: int +eLaunchFlagDisableASLR: int +eLaunchFlagDisableSTDIO: int +eLaunchFlagLaunchInTTY: int +eLaunchFlagLaunchInShell: int +eLaunchFlagLaunchInSeparateProcessGroup: int +eLaunchFlagDontSetExitStatus: int +eLaunchFlagDetachOnError: int +eLaunchFlagShellExpandArguments: int +eLaunchFlagCloseTTYOnExit: int +eLaunchFlagInheritTCCFromParent: int +eOnlyThisThread: int +eAllThreads: int +eOnlyDuringStepping: int +eByteOrderInvalid: int +eByteOrderBig: int +eByteOrderPDP: int +eByteOrderLittle: int +eEncodingInvalid: int +eEncodingUint: int +eEncodingSint: int +eEncodingIEEE754: int +eEncodingVector: int +eFormatDefault: int +eFormatInvalid: int +eFormatBoolean: int +eFormatBinary: int +eFormatBytes: int +eFormatBytesWithASCII: int +eFormatChar: int +eFormatCharPrintable: int +eFormatComplex: int +eFormatComplexFloat: int +eFormatCString: int +eFormatDecimal: int +eFormatEnum: int +eFormatHex: int +eFormatHexUppercase: int +eFormatFloat: int +eFormatOctal: int +eFormatOSType: int +eFormatUnicode16: int +eFormatUnicode32: int +eFormatUnsigned: int +eFormatPointer: int +eFormatVectorOfChar: int +eFormatVectorOfSInt8: int +eFormatVectorOfUInt8: int +eFormatVectorOfSInt16: int +eFormatVectorOfUInt16: int +eFormatVectorOfSInt32: int +eFormatVectorOfUInt32: int +eFormatVectorOfSInt64: int +eFormatVectorOfUInt64: int +eFormatVectorOfFloat16: int +eFormatVectorOfFloat32: int +eFormatVectorOfFloat64: int +eFormatVectorOfUInt128: int +eFormatComplexInteger: int +eFormatCharArray: int +eFormatAddressInfo: int +eFormatHexFloat: int +eFormatInstruction: int +eFormatVoid: int +eFormatUnicode8: int +kNumFormats: int +eDescriptionLevelBrief: int +eDescriptionLevelFull: int +eDescriptionLevelVerbose: int +eDescriptionLevelInitial: int +kNumDescriptionLevels: int +eScriptLanguageNone: int +eScriptLanguagePython: int +eScriptLanguageLua: int +eScriptLanguageUnknown: int +eScriptLanguageDefault: int +eRegisterKindEHFrame: int +eRegisterKindDWARF: int +eRegisterKindGeneric: int +eRegisterKindProcessPlugin: int +eRegisterKindLLDB: int +kNumRegisterKinds: int +eStopReasonInvalid: int +eStopReasonNone: int +eStopReasonTrace: int +eStopReasonBreakpoint: int +eStopReasonWatchpoint: int +eStopReasonSignal: int +eStopReasonException: int +eStopReasonExec: int +eStopReasonPlanComplete: int +eStopReasonThreadExiting: int +eStopReasonInstrumentation: int +eStopReasonProcessorTrace: int +eStopReasonFork: int +eStopReasonVFork: int +eStopReasonVForkDone: int +eStopReasonInterrupt: int +eReturnStatusInvalid: int +eReturnStatusSuccessFinishNoResult: int +eReturnStatusSuccessFinishResult: int +eReturnStatusSuccessContinuingNoResult: int +eReturnStatusSuccessContinuingResult: int +eReturnStatusStarted: int +eReturnStatusFailed: int +eReturnStatusQuit: int +eExpressionCompleted: int +eExpressionSetupError: int +eExpressionParseError: int +eExpressionDiscarded: int +eExpressionInterrupted: int +eExpressionHitBreakpoint: int +eExpressionTimedOut: int +eExpressionResultUnavailable: int +eExpressionStoppedForDebug: int +eExpressionThreadVanished: int +eSearchDepthInvalid: int +eSearchDepthTarget: int +eSearchDepthModule: int +eSearchDepthCompUnit: int +eSearchDepthFunction: int +eSearchDepthBlock: int +eSearchDepthAddress: int +kLastSearchDepthKind: int +eConnectionStatusSuccess: int +eConnectionStatusEndOfFile: int +eConnectionStatusError: int +eConnectionStatusTimedOut: int +eConnectionStatusNoConnection: int +eConnectionStatusLostConnection: int +eConnectionStatusInterrupted: int +eErrorTypeInvalid: int +eErrorTypeGeneric: int +eErrorTypeMachKernel: int +eErrorTypePOSIX: int +eErrorTypeExpression: int +eErrorTypeWin32: int +eValueTypeInvalid: int +eValueTypeVariableGlobal: int +eValueTypeVariableStatic: int +eValueTypeVariableArgument: int +eValueTypeVariableLocal: int +eValueTypeRegister: int +eValueTypeRegisterSet: int +eValueTypeConstResult: int +eValueTypeVariableThreadLocal: int +eValueTypeVTable: int +eValueTypeVTableEntry: int +eInputReaderGranularityInvalid: int +eInputReaderGranularityByte: int +eInputReaderGranularityWord: int +eInputReaderGranularityLine: int +eInputReaderGranularityAll: int +eSymbolContextTarget: int +eSymbolContextModule: int +eSymbolContextCompUnit: int +eSymbolContextFunction: int +eSymbolContextBlock: int +eSymbolContextLineEntry: int +eSymbolContextSymbol: int +eSymbolContextEverything: int +eSymbolContextVariable: int +eSymbolContextLastItem: int +ePermissionsWritable: int +ePermissionsReadable: int +ePermissionsExecutable: int +eInputReaderActivate: int +eInputReaderAsynchronousOutputWritten: int +eInputReaderReactivate: int +eInputReaderDeactivate: int +eInputReaderGotToken: int +eInputReaderInterrupt: int +eInputReaderEndOfFile: int +eInputReaderDone: int +eBreakpointEventTypeInvalidType: int +eBreakpointEventTypeAdded: int +eBreakpointEventTypeRemoved: int +eBreakpointEventTypeLocationsAdded: int +eBreakpointEventTypeLocationsRemoved: int +eBreakpointEventTypeLocationsResolved: int +eBreakpointEventTypeEnabled: int +eBreakpointEventTypeDisabled: int +eBreakpointEventTypeCommandChanged: int +eBreakpointEventTypeConditionChanged: int +eBreakpointEventTypeIgnoreChanged: int +eBreakpointEventTypeThreadChanged: int +eBreakpointEventTypeAutoContinueChanged: int +eWatchpointEventTypeInvalidType: int +eWatchpointEventTypeAdded: int +eWatchpointEventTypeRemoved: int +eWatchpointEventTypeEnabled: int +eWatchpointEventTypeDisabled: int +eWatchpointEventTypeCommandChanged: int +eWatchpointEventTypeConditionChanged: int +eWatchpointEventTypeIgnoreChanged: int +eWatchpointEventTypeThreadChanged: int +eWatchpointEventTypeTypeChanged: int +eWatchpointWriteTypeDisabled: int +eWatchpointWriteTypeAlways: int +eWatchpointWriteTypeOnModify: int +eLanguageTypeUnknown: int +eLanguageTypeC89: int +eLanguageTypeC: int +eLanguageTypeAda83: int +eLanguageTypeC_plus_plus: int +eLanguageTypeCobol74: int +eLanguageTypeCobol85: int +eLanguageTypeFortran77: int +eLanguageTypeFortran90: int +eLanguageTypePascal83: int +eLanguageTypeModula2: int +eLanguageTypeJava: int +eLanguageTypeC99: int +eLanguageTypeAda95: int +eLanguageTypeFortran95: int +eLanguageTypePLI: int +eLanguageTypeObjC: int +eLanguageTypeObjC_plus_plus: int +eLanguageTypeUPC: int +eLanguageTypeD: int +eLanguageTypePython: int +eLanguageTypeOpenCL: int +eLanguageTypeGo: int +eLanguageTypeModula3: int +eLanguageTypeHaskell: int +eLanguageTypeC_plus_plus_03: int +eLanguageTypeC_plus_plus_11: int +eLanguageTypeOCaml: int +eLanguageTypeRust: int +eLanguageTypeC11: int +eLanguageTypeSwift: int +eLanguageTypeJulia: int +eLanguageTypeDylan: int +eLanguageTypeC_plus_plus_14: int +eLanguageTypeFortran03: int +eLanguageTypeFortran08: int +eLanguageTypeRenderScript: int +eLanguageTypeBLISS: int +eLanguageTypeKotlin: int +eLanguageTypeZig: int +eLanguageTypeCrystal: int +eLanguageTypeC_plus_plus_17: int +eLanguageTypeC_plus_plus_20: int +eLanguageTypeC17: int +eLanguageTypeFortran18: int +eLanguageTypeAda2005: int +eLanguageTypeAda2012: int +eLanguageTypeHIP: int +eLanguageTypeAssembly: int +eLanguageTypeC_sharp: int +eLanguageTypeMojo: int +eLanguageTypeMipsAssembler: int +eNumLanguageTypes: int +eInstrumentationRuntimeTypeAddressSanitizer: int +eInstrumentationRuntimeTypeThreadSanitizer: int +eInstrumentationRuntimeTypeUndefinedBehaviorSanitizer: int +eInstrumentationRuntimeTypeMainThreadChecker: int +eInstrumentationRuntimeTypeSwiftRuntimeReporting: int +eInstrumentationRuntimeTypeLibsanitizersAsan: int +eNumInstrumentationRuntimeTypes: int +eNoDynamicValues: int +eDynamicCanRunTarget: int +eDynamicDontRunTarget: int +eStopShowColumnAnsiOrCaret: int +eStopShowColumnAnsi: int +eStopShowColumnCaret: int +eStopShowColumnNone: int +eAccessNone: int +eAccessPublic: int +eAccessPrivate: int +eAccessProtected: int +eAccessPackage: int +eArgTypeAddress: int +eArgTypeAddressOrExpression: int +eArgTypeAliasName: int +eArgTypeAliasOptions: int +eArgTypeArchitecture: int +eArgTypeBoolean: int +eArgTypeBreakpointID: int +eArgTypeBreakpointIDRange: int +eArgTypeBreakpointName: int +eArgTypeByteSize: int +eArgTypeClassName: int +eArgTypeCommandName: int +eArgTypeCount: int +eArgTypeDescriptionVerbosity: int +eArgTypeDirectoryName: int +eArgTypeDisassemblyFlavor: int +eArgTypeEndAddress: int +eArgTypeExpression: int +eArgTypeExpressionPath: int +eArgTypeExprFormat: int +eArgTypeFileLineColumn: int +eArgTypeFilename: int +eArgTypeFormat: int +eArgTypeFrameIndex: int +eArgTypeFullName: int +eArgTypeFunctionName: int +eArgTypeFunctionOrSymbol: int +eArgTypeGDBFormat: int +eArgTypeHelpText: int +eArgTypeIndex: int +eArgTypeLanguage: int +eArgTypeLineNum: int +eArgTypeLogCategory: int +eArgTypeLogChannel: int +eArgTypeMethod: int +eArgTypeName: int +eArgTypeNewPathPrefix: int +eArgTypeNumLines: int +eArgTypeNumberPerLine: int +eArgTypeOffset: int +eArgTypeOldPathPrefix: int +eArgTypeOneLiner: int +eArgTypePath: int +eArgTypePermissionsNumber: int +eArgTypePermissionsString: int +eArgTypePid: int +eArgTypePlugin: int +eArgTypeProcessName: int +eArgTypePythonClass: int +eArgTypePythonFunction: int +eArgTypePythonScript: int +eArgTypeQueueName: int +eArgTypeRegisterName: int +eArgTypeRegularExpression: int +eArgTypeRunArgs: int +eArgTypeRunMode: int +eArgTypeScriptedCommandSynchronicity: int +eArgTypeScriptLang: int +eArgTypeSearchWord: int +eArgTypeSelector: int +eArgTypeSettingIndex: int +eArgTypeSettingKey: int +eArgTypeSettingPrefix: int +eArgTypeSettingVariableName: int +eArgTypeShlibName: int +eArgTypeSourceFile: int +eArgTypeSortOrder: int +eArgTypeStartAddress: int +eArgTypeSummaryString: int +eArgTypeSymbol: int +eArgTypeThreadID: int +eArgTypeThreadIndex: int +eArgTypeThreadName: int +eArgTypeTypeName: int +eArgTypeUnsignedInteger: int +eArgTypeUnixSignal: int +eArgTypeVarName: int +eArgTypeValue: int +eArgTypeWidth: int +eArgTypeNone: int +eArgTypePlatform: int +eArgTypeWatchpointID: int +eArgTypeWatchpointIDRange: int +eArgTypeWatchType: int +eArgRawInput: int +eArgTypeCommand: int +eArgTypeColumnNum: int +eArgTypeModuleUUID: int +eArgTypeSaveCoreStyle: int +eArgTypeLogHandler: int +eArgTypeSEDStylePair: int +eArgTypeRecognizerID: int +eArgTypeConnectURL: int +eArgTypeTargetID: int +eArgTypeStopHookID: int +eArgTypeCompletionType: int +eArgTypeRemotePath: int +eArgTypeRemoteFilename: int +eArgTypeModule: int +eArgTypeCPUName: int +eArgTypeCPUFeatures: int +eArgTypeLastArg: int +eSymbolTypeAny: int +eSymbolTypeInvalid: int +eSymbolTypeAbsolute: int +eSymbolTypeCode: int +eSymbolTypeResolver: int +eSymbolTypeData: int +eSymbolTypeTrampoline: int +eSymbolTypeRuntime: int +eSymbolTypeException: int +eSymbolTypeSourceFile: int +eSymbolTypeHeaderFile: int +eSymbolTypeObjectFile: int +eSymbolTypeCommonBlock: int +eSymbolTypeBlock: int +eSymbolTypeLocal: int +eSymbolTypeParam: int +eSymbolTypeVariable: int +eSymbolTypeVariableType: int +eSymbolTypeLineEntry: int +eSymbolTypeLineHeader: int +eSymbolTypeScopeBegin: int +eSymbolTypeScopeEnd: int +eSymbolTypeAdditional: int +eSymbolTypeCompiler: int +eSymbolTypeInstrumentation: int +eSymbolTypeUndefined: int +eSymbolTypeObjCClass: int +eSymbolTypeObjCMetaClass: int +eSymbolTypeObjCIVar: int +eSymbolTypeReExported: int +eSectionTypeInvalid: int +eSectionTypeCode: int +eSectionTypeContainer: int +eSectionTypeData: int +eSectionTypeDataCString: int +eSectionTypeDataCStringPointers: int +eSectionTypeDataSymbolAddress: int +eSectionTypeData4: int +eSectionTypeData8: int +eSectionTypeData16: int +eSectionTypeDataPointers: int +eSectionTypeDebug: int +eSectionTypeZeroFill: int +eSectionTypeDataObjCMessageRefs: int +eSectionTypeDataObjCCFStrings: int +eSectionTypeDWARFDebugAbbrev: int +eSectionTypeDWARFDebugAddr: int +eSectionTypeDWARFDebugAranges: int +eSectionTypeDWARFDebugCuIndex: int +eSectionTypeDWARFDebugFrame: int +eSectionTypeDWARFDebugInfo: int +eSectionTypeDWARFDebugLine: int +eSectionTypeDWARFDebugLoc: int +eSectionTypeDWARFDebugMacInfo: int +eSectionTypeDWARFDebugMacro: int +eSectionTypeDWARFDebugPubNames: int +eSectionTypeDWARFDebugPubTypes: int +eSectionTypeDWARFDebugRanges: int +eSectionTypeDWARFDebugStr: int +eSectionTypeDWARFDebugStrOffsets: int +eSectionTypeDWARFAppleNames: int +eSectionTypeDWARFAppleTypes: int +eSectionTypeDWARFAppleNamespaces: int +eSectionTypeDWARFAppleObjC: int +eSectionTypeELFSymbolTable: int +eSectionTypeELFDynamicSymbols: int +eSectionTypeELFRelocationEntries: int +eSectionTypeELFDynamicLinkInfo: int +eSectionTypeEHFrame: int +eSectionTypeARMexidx: int +eSectionTypeARMextab: int +eSectionTypeCompactUnwind: int +eSectionTypeGoSymtab: int +eSectionTypeAbsoluteAddress: int +eSectionTypeDWARFGNUDebugAltLink: int +eSectionTypeDWARFDebugTypes: int +eSectionTypeDWARFDebugNames: int +eSectionTypeOther: int +eSectionTypeDWARFDebugLineStr: int +eSectionTypeDWARFDebugRngLists: int +eSectionTypeDWARFDebugLocLists: int +eSectionTypeDWARFDebugAbbrevDwo: int +eSectionTypeDWARFDebugInfoDwo: int +eSectionTypeDWARFDebugStrDwo: int +eSectionTypeDWARFDebugStrOffsetsDwo: int +eSectionTypeDWARFDebugTypesDwo: int +eSectionTypeDWARFDebugRngListsDwo: int +eSectionTypeDWARFDebugLocDwo: int +eSectionTypeDWARFDebugLocListsDwo: int +eSectionTypeDWARFDebugTuIndex: int +eSectionTypeCTF: int +eSectionTypeLLDBTypeSummaries: int +eSectionTypeLLDBFormatters: int +eSectionTypeSwiftModules: int +eEmulateInstructionOptionNone: int +eEmulateInstructionOptionAutoAdvancePC: int +eEmulateInstructionOptionIgnoreConditions: int +eFunctionNameTypeNone: int +eFunctionNameTypeAuto: int +eFunctionNameTypeFull: int +eFunctionNameTypeBase: int +eFunctionNameTypeMethod: int +eFunctionNameTypeSelector: int +eFunctionNameTypeAny: int +eBasicTypeInvalid: int +eBasicTypeVoid: int +eBasicTypeChar: int +eBasicTypeSignedChar: int +eBasicTypeUnsignedChar: int +eBasicTypeWChar: int +eBasicTypeSignedWChar: int +eBasicTypeUnsignedWChar: int +eBasicTypeChar16: int +eBasicTypeChar32: int +eBasicTypeChar8: int +eBasicTypeShort: int +eBasicTypeUnsignedShort: int +eBasicTypeInt: int +eBasicTypeUnsignedInt: int +eBasicTypeLong: int +eBasicTypeUnsignedLong: int +eBasicTypeLongLong: int +eBasicTypeUnsignedLongLong: int +eBasicTypeInt128: int +eBasicTypeUnsignedInt128: int +eBasicTypeBool: int +eBasicTypeHalf: int +eBasicTypeFloat: int +eBasicTypeDouble: int +eBasicTypeLongDouble: int +eBasicTypeFloatComplex: int +eBasicTypeDoubleComplex: int +eBasicTypeLongDoubleComplex: int +eBasicTypeObjCID: int +eBasicTypeObjCClass: int +eBasicTypeObjCSel: int +eBasicTypeNullPtr: int +eBasicTypeOther: int +eTraceTypeNone: int +eTraceTypeProcessorTrace: int +eStructuredDataTypeInvalid: int +eStructuredDataTypeNull: int +eStructuredDataTypeGeneric: int +eStructuredDataTypeArray: int +eStructuredDataTypeInteger: int +eStructuredDataTypeFloat: int +eStructuredDataTypeBoolean: int +eStructuredDataTypeString: int +eStructuredDataTypeDictionary: int +eStructuredDataTypeSignedInteger: int +eStructuredDataTypeUnsignedInteger: int +eTypeClassInvalid: int +eTypeClassArray: int +eTypeClassBlockPointer: int +eTypeClassBuiltin: int +eTypeClassClass: int +eTypeClassComplexFloat: int +eTypeClassComplexInteger: int +eTypeClassEnumeration: int +eTypeClassFunction: int +eTypeClassMemberPointer: int +eTypeClassObjCObject: int +eTypeClassObjCInterface: int +eTypeClassObjCObjectPointer: int +eTypeClassPointer: int +eTypeClassReference: int +eTypeClassStruct: int +eTypeClassTypedef: int +eTypeClassUnion: int +eTypeClassVector: int +eTypeClassOther: int +eTypeClassAny: int +eTemplateArgumentKindNull: int +eTemplateArgumentKindType: int +eTemplateArgumentKindDeclaration: int +eTemplateArgumentKindIntegral: int +eTemplateArgumentKindTemplate: int +eTemplateArgumentKindTemplateExpansion: int +eTemplateArgumentKindExpression: int +eTemplateArgumentKindPack: int +eTemplateArgumentKindNullPtr: int +eTemplateArgumentKindStructuralValue: int +eFormatterMatchExact: int +eFormatterMatchRegex: int +eFormatterMatchCallback: int +eLastFormatterMatchType: int +eTypeOptionNone: int +eTypeOptionCascade: int +eTypeOptionSkipPointers: int +eTypeOptionSkipReferences: int +eTypeOptionHideChildren: int +eTypeOptionHideValue: int +eTypeOptionShowOneLiner: int +eTypeOptionHideNames: int +eTypeOptionNonCacheable: int +eTypeOptionHideEmptyAggregates: int +eTypeOptionFrontEndWantsDereference: int +eFrameCompareInvalid: int +eFrameCompareUnknown: int +eFrameCompareEqual: int +eFrameCompareSameParent: int +eFrameCompareYounger: int +eFrameCompareOlder: int +eFilePermissionsUserRead: int +eFilePermissionsUserWrite: int +eFilePermissionsUserExecute: int +eFilePermissionsGroupRead: int +eFilePermissionsGroupWrite: int +eFilePermissionsGroupExecute: int +eFilePermissionsWorldRead: int +eFilePermissionsWorldWrite: int +eFilePermissionsWorldExecute: int +eFilePermissionsUserRW: int +eFileFilePermissionsUserRX: int +eFilePermissionsUserRWX: int +eFilePermissionsGroupRW: int +eFilePermissionsGroupRX: int +eFilePermissionsGroupRWX: int +eFilePermissionsWorldRW: int +eFilePermissionsWorldRX: int +eFilePermissionsWorldRWX: int +eFilePermissionsEveryoneR: int +eFilePermissionsEveryoneW: int +eFilePermissionsEveryoneX: int +eFilePermissionsEveryoneRW: int +eFilePermissionsEveryoneRX: int +eFilePermissionsEveryoneRWX: int +eFilePermissionsFileDefault: int +eFilePermissionsDirectoryDefault: int +eQueueItemKindUnknown: int +eQueueItemKindFunction: int +eQueueItemKindBlock: int +eQueueKindUnknown: int +eQueueKindSerial: int +eQueueKindConcurrent: int +eExpressionEvaluationParse: int +eExpressionEvaluationIRGen: int +eExpressionEvaluationExecution: int +eExpressionEvaluationComplete: int +eInstructionControlFlowKindUnknown: int +eInstructionControlFlowKindOther: int +eInstructionControlFlowKindCall: int +eInstructionControlFlowKindReturn: int +eInstructionControlFlowKindJump: int +eInstructionControlFlowKindCondJump: int +eInstructionControlFlowKindFarCall: int +eInstructionControlFlowKindFarReturn: int +eInstructionControlFlowKindFarJump: int +eWatchpointKindWrite: int +eWatchpointKindRead: int +eGdbSignalBadAccess: int +eGdbSignalBadInstruction: int +eGdbSignalArithmetic: int +eGdbSignalEmulation: int +eGdbSignalSoftware: int +eGdbSignalBreakpoint: int +ePathTypeLLDBShlibDir: int +ePathTypeSupportExecutableDir: int +ePathTypeHeaderDir: int +ePathTypePythonDir: int +ePathTypeLLDBSystemPlugins: int +ePathTypeLLDBUserPlugins: int +ePathTypeLLDBTempSystemDir: int +ePathTypeGlobalLLDBTempSystemDir: int +ePathTypeClangDir: int +eMemberFunctionKindUnknown: int +eMemberFunctionKindConstructor: int +eMemberFunctionKindDestructor: int +eMemberFunctionKindInstanceMethod: int +eMemberFunctionKindStaticMethod: int +eMatchTypeNormal: int +eMatchTypeRegex: int +eMatchTypeStartsWith: int +eMatchTypeRegexInsensitive: int +eTypeHasChildren: int +eTypeHasValue: int +eTypeIsArray: int +eTypeIsBlock: int +eTypeIsBuiltIn: int +eTypeIsClass: int +eTypeIsCPlusPlus: int +eTypeIsEnumeration: int +eTypeIsFuncPrototype: int +eTypeIsMember: int +eTypeIsObjC: int +eTypeIsPointer: int +eTypeIsReference: int +eTypeIsStructUnion: int +eTypeIsTemplate: int +eTypeIsTypedef: int +eTypeIsVector: int +eTypeIsScalar: int +eTypeIsInteger: int +eTypeIsFloat: int +eTypeIsComplex: int +eTypeIsSigned: int +eTypeInstanceIsPointer: int +eCommandRequiresTarget: int +eCommandRequiresProcess: int +eCommandRequiresThread: int +eCommandRequiresFrame: int +eCommandRequiresRegContext: int +eCommandTryTargetAPILock: int +eCommandProcessMustBeLaunched: int +eCommandProcessMustBePaused: int +eCommandProcessMustBeTraced: int +eTypeSummaryCapped: bool +eTypeSummaryUncapped: bool +eCommandInterpreterResultSuccess: int +eCommandInterpreterResultInferiorCrash: int +eCommandInterpreterResultCommandError: int +eCommandInterpreterResultQuitRequested: int +eSaveCoreUnspecified: int +eSaveCoreFull: int +eSaveCoreDirtyOnly: int +eSaveCoreStackOnly: int +eSaveCoreCustomOnly: int +eTraceEventDisabledSW: int +eTraceEventDisabledHW: int +eTraceEventCPUChanged: int +eTraceEventHWClockTick: int +eTraceEventSyncPoint: int +eTraceItemKindError: int +eTraceItemKindEvent: int +eTraceItemKindInstruction: int +eTraceCursorSeekTypeBeginning: int +eTraceCursorSeekTypeCurrent: int +eTraceCursorSeekTypeEnd: int +eDWIMPrintVerbosityNone: int +eDWIMPrintVerbosityExpression: int +eDWIMPrintVerbosityFull: int +eWatchPointValueKindInvalid: int +eWatchPointValueKindVariable: int +eWatchPointValueKindExpression: int +eNoCompletion: int +eSourceFileCompletion: int +eDiskFileCompletion: int +eDiskDirectoryCompletion: int +eSymbolCompletion: int +eModuleCompletion: int +eSettingsNameCompletion: int +ePlatformPluginCompletion: int +eArchitectureCompletion: int +eVariablePathCompletion: int +eRegisterCompletion: int +eBreakpointCompletion: int +eProcessPluginCompletion: int +eDisassemblyFlavorCompletion: int +eTypeLanguageCompletion: int +eFrameIndexCompletion: int +eModuleUUIDCompletion: int +eStopHookIDCompletion: int +eThreadIndexCompletion: int +eWatchpointIDCompletion: int +eBreakpointNameCompletion: int +eProcessIDCompletion: int +eProcessNameCompletion: int +eRemoteDiskFileCompletion: int +eRemoteDiskDirectoryCompletion: int +eTypeCategoryNameCompletion: int +eCustomCompletion: int +eThreadIDCompletion: int +eTerminatorCompletion: int +eRefetch: int +eReuse: int +eSymbolDownloadOff: int +eSymbolDownloadBackground: int +eSymbolDownloadForeground: int +eAddressMaskTypeCode: int +eAddressMaskTypeData: int +eAddressMaskTypeAny: int +eAddressMaskTypeAll: int +eAddressMaskRangeLow: int +eAddressMaskRangeHigh: int +eAddressMaskRangeAny: int +eAddressMaskRangeAll: int +eBroadcastBitProgress: int +eBroadcastBitWarning: int +eBroadcastBitError: int +eBroadcastSymbolChange: int +eBroadcastBitProgressCategory: int +eBroadcastBitExternalProgress: int +eBroadcastBitExternalProgressCategory: int +eSeverityError: int +eSeverityWarning: int +eSeverityInfo: int + +class SBAddress: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def __ne__(self, rhs: Any) -> bool: ... + def IsValid(self) -> bool: ... + def Clear(self) -> None: ... + def GetFileAddress(self) -> int: ... + def GetLoadAddress(self, target: SBTarget) -> int: ... + def SetAddress(self, section: SBSection, offset: int) -> None: ... + def SetLoadAddress(self, load_addr: int, target: SBTarget) -> None: ... + def OffsetAddress(self, offset: int) -> bool: ... + def GetDescription(self, description: SBStream) -> bool: ... + def GetSymbolContext(self, resolve_scope: int) -> SBSymbolContext: ... + def GetSection(self) -> SBSection: ... + def GetOffset(self) -> int: ... + def GetModule(self) -> SBModule: ... + def GetCompileUnit(self) -> SBCompileUnit: ... + def GetFunction(self) -> SBFunction: ... + def GetBlock(self) -> SBBlock: ... + def GetSymbol(self) -> SBSymbol: ... + def GetLineEntry(self) -> SBLineEntry: ... + def __eq__(self, other: Any) -> bool: ... + def __get_load_addr_property__(self) -> int: ... + def __set_load_addr_property__(self, load_addr: int) -> None: ... + def __int__(self) -> int: ... + def __oct__(self) -> Incomplete: ... + def __hex__(self) -> Incomplete: ... + module: SBModule + compile_unit: SBCompileUnit + line_entry: SBLineEntry + function: SBFunction + block: SBBlock + symbol: SBSymbol + offset: int + section: SBSection + file_addr: int + load_addr: int + +class SBAddressRange: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def Clear(self) -> None: ... + def IsValid(self) -> bool: ... + def GetBaseAddress(self) -> SBAddress: ... + def GetByteSize(self) -> int: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def GetDescription(self, description: SBStream, target: SBTarget) -> bool: ... + +class SBAddressRangeList: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetSize(self) -> int: ... + def Clear(self) -> None: ... + def GetAddressRangeAtIndex(self, idx: int) -> SBAddressRange: ... + def Append(self, addr_range: Union[SBAddressRange, SBAddressRangeList]) -> None: ... + def GetDescription(self, description: SBStream, target: SBTarget) -> bool: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[SBAddressRange]: ... + def __getitem__(self, idx: int) -> SBAddressRange: ... + +class SBAttachInfo: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetProcessID(self) -> int: ... + def SetProcessID(self, pid: int) -> None: ... + def SetExecutable(self, arg: Union[str, SBFileSpec]) -> None: ... + def GetWaitForLaunch(self) -> bool: ... + def SetWaitForLaunch(self, b: bool, _async: Union[bool, None] = None) -> None: ... + def GetIgnoreExisting(self) -> bool: ... + def SetIgnoreExisting(self, b: bool) -> None: ... + def GetResumeCount(self) -> int: ... + def SetResumeCount(self, c: int) -> None: ... + def GetProcessPluginName(self) -> str: ... + def SetProcessPluginName(self, plugin_name: str) -> None: ... + def GetUserID(self) -> int: ... + def GetGroupID(self) -> int: ... + def UserIDIsValid(self) -> bool: ... + def GroupIDIsValid(self) -> bool: ... + def SetUserID(self, uid: int) -> None: ... + def SetGroupID(self, gid: int) -> None: ... + def GetEffectiveUserID(self) -> int: ... + def GetEffectiveGroupID(self) -> int: ... + def EffectiveUserIDIsValid(self) -> bool: ... + def EffectiveGroupIDIsValid(self) -> bool: ... + def SetEffectiveUserID(self, uid: int) -> None: ... + def SetEffectiveGroupID(self, gid: int) -> None: ... + def GetParentProcessID(self) -> int: ... + def SetParentProcessID(self, pid: int) -> None: ... + def ParentProcessIDIsValid(self) -> bool: ... + def GetListener(self) -> SBListener: ... + def SetListener(self, listener: SBListener) -> None: ... + def GetShadowListener(self) -> SBListener: ... + def SetShadowListener(self, listener: SBListener) -> None: ... + def GetScriptedProcessClassName(self) -> str: ... + def SetScriptedProcessClassName(self, class_name: str) -> None: ... + def GetScriptedProcessDictionary(self) -> SBStructuredData: ... + def SetScriptedProcessDictionary(self, dict: SBStructuredData) -> None: ... + +class SBBlock: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def IsInlined(self) -> bool: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetInlinedName(self) -> Union[str, None]: ... + def GetInlinedCallSiteFile(self) -> SBFileSpec: ... + def GetInlinedCallSiteLine(self) -> int: ... + def GetInlinedCallSiteColumn(self) -> int: ... + def GetParent(self) -> SBBlock: ... + def GetSibling(self) -> SBBlock: ... + def GetFirstChild(self) -> SBBlock: ... + def GetNumRanges(self) -> int: ... + def GetRangeStartAddress(self, idx: int) -> SBAddress: ... + def GetRangeEndAddress(self, idx: int) -> SBAddress: ... + def GetRanges(self) -> SBAddressRangeList: ... + def GetRangeIndexForBlockAddress(self, block_addr: SBAddress) -> int: ... + def GetVariables( + self, + frm_or_tgt: Union[SBFrame, SBTarget], + args: bool, + locs: bool, + stats: bool, + use_dynamic: Union[Incomplete, None], + ) -> SBValueList: ... + def GetContainingInlinedBlock(self) -> SBBlock: ... + def GetDescription(self, description: SBStream) -> bool: ... + def get_range_at_index(self, idx: int) -> SBAddressRange: ... + + class ranges_access: + sbblock: SBBlock + def __init__(self, sbblock: SBBlock) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + + def get_ranges_access_object(self) -> ranges_access: ... + ranges_array: list[SBAddressRange] + def get_ranges_array(self) -> list[SBAddressRange]: ... + def get_call_site(self) -> declaration: ... + parent: SBBlock + first_child: SBBlock + call_site: declaration + sibling: SBBlock + name: Union[str, None] + inlined_block: SBBlock + range: Incomplete + ranges: list[SBAddressRange] + num_ranges: int + +class SBBreakpoint: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def GetID(self) -> int: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def ClearAllBreakpointSites(self) -> None: ... + def GetTarget(self) -> SBTarget: ... + def FindLocationByAddress(self, vm_addr: int) -> SBBreakpointLocation: ... + def FindLocationIDByAddress(self, vm_addr: int) -> int: ... + def FindLocationByID(self, bp_loc_id: int) -> SBBreakpointLocation: ... + def GetLocationAtIndex(self, index: int) -> SBBreakpointLocation: ... + def SetEnabled(self, enable: bool) -> None: ... + def IsEnabled(self) -> bool: ... + def SetOneShot(self, one_shot: bool) -> None: ... + def IsOneShot(self) -> bool: ... + def IsInternal(self) -> bool: ... + def GetHitCount(self) -> int: ... + def SetIgnoreCount(self, count: int) -> None: ... + def GetIgnoreCount(self) -> int: ... + def SetCondition(self, condition: str) -> None: ... + def GetCondition(self) -> str: ... + def SetAutoContinue(self, auto_continue: bool) -> None: ... + def GetAutoContinue(self) -> bool: ... + def SetThreadID(self, sb_thread_id: int) -> None: ... + def GetThreadID(self) -> int: ... + def SetThreadIndex(self, index: int) -> None: ... + def GetThreadIndex(self) -> int: ... + def SetThreadName(self, thread_name: str) -> None: ... + def GetThreadName(self) -> str: ... + def SetQueueName(self, queue_name: str) -> None: ... + def GetQueueName(self) -> str: ... + def SetScriptCallbackFunction( + self, callback_function_name: str, extra_args: Union[SBStructuredData, None] = None + ) -> Union[SBError, None]: ... + def SetCommandLineCommands(self, commands: SBStringList) -> None: ... + def GetCommandLineCommands(self, commands: SBStringList) -> bool: ... + def SetScriptCallbackBody(self, script_body_text: str) -> SBError: ... + def AddName(self, new_name: str) -> bool: ... + def AddNameWithErrorHandling(self, new_name: str) -> SBError: ... + def RemoveName(self, name_to_remove: str) -> None: ... + def MatchesName(self, name: str) -> bool: ... + def GetNames(self, names: SBStringList) -> None: ... + def GetNumResolvedLocations(self) -> int: ... + def GetNumLocations(self) -> int: ... + def GetDescription(self, description: SBStream, include_locations: Union[bool, None] = None) -> bool: ... + @staticmethod + def EventIsBreakpointEvent(event: SBEvent) -> bool: ... + @staticmethod + def GetBreakpointEventTypeFromEvent(event: SBEvent) -> int: ... + @staticmethod + def GetBreakpointFromEvent(event: SBEvent) -> SBBreakpoint: ... + @staticmethod + def GetBreakpointLocationAtIndexFromEvent(event: SBEvent, loc_idx: int) -> SBBreakpointLocation: ... + @staticmethod + def GetNumBreakpointLocationsFromEvent(event_sp: SBEvent) -> int: ... + def IsHardware(self) -> bool: ... + def AddLocation(self, address: SBAddress) -> SBError: ... + def SerializeToStructuredData(self) -> SBStructuredData: ... + + class locations_access: + sbbreakpoint: SBBreakpoint + def __init__(self, sbbreakpoint: SBBreakpoint) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + + def get_locations_access_object(self) -> locations_access: ... + def get_breakpoint_location_list(self) -> list[SBBreakpointLocation]: ... + def __iter__(self) -> Iterator[SBBreakpointLocation]: ... + def __len__(self) -> int: ... + locations: list[SBBreakpointLocation] + location: Incomplete + id: int + enabled: bool + one_shot: bool + num_locations: int + +class SBBreakpointList: + thisown: Incomplete + def __init__(self, target: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetSize(self) -> int: ... + def GetBreakpointAtIndex(self, idx: int) -> SBBreakpoint: ... + def FindBreakpointByID(self, arg2: int) -> SBBreakpoint: ... + def Append(self, sb_bkpt: SBBreakpoint) -> None: ... + def AppendIfUnique(self, sb_bkpt: SBBreakpoint) -> bool: ... + def AppendByID(self, id: int) -> None: ... + def Clear(self) -> None: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[SBBreakpoint]: ... + +class SBBreakpointLocation: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetID(self) -> int: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetAddress(self) -> SBAddress: ... + def GetLoadAddress(self) -> int: ... + def SetEnabled(self, enabled: bool) -> None: ... + def IsEnabled(self) -> bool: ... + def GetHitCount(self) -> int: ... + def GetIgnoreCount(self) -> int: ... + def SetIgnoreCount(self, n: int) -> None: ... + def SetCondition(self, condition: str) -> None: ... + def GetCondition(self) -> str: ... + def SetAutoContinue(self, auto_continue: bool) -> None: ... + def GetAutoContinue(self) -> bool: ... + def SetScriptCallbackFunction( + self, callback_function_name: str, extra_args: Union[SBStructuredData, None] = None + ) -> Union[SBError, None]: ... + def SetScriptCallbackBody(self, script_body_text: str) -> SBError: ... + def SetCommandLineCommands(self, commands: SBStringList) -> None: ... + def GetCommandLineCommands(self, commands: SBStringList) -> bool: ... + def SetThreadID(self, sb_thread_id: int) -> None: ... + def GetThreadID(self) -> int: ... + def SetThreadIndex(self, index: int) -> None: ... + def GetThreadIndex(self) -> int: ... + def SetThreadName(self, thread_name: str) -> None: ... + def GetThreadName(self) -> str: ... + def SetQueueName(self, queue_name: str) -> None: ... + def GetQueueName(self) -> str: ... + def IsResolved(self) -> bool: ... + def GetDescription(self, description: SBStream, level: int) -> bool: ... + def GetBreakpoint(self) -> SBBreakpoint: ... + def __eq__(self, other: Any) -> bool: ... + +class SBBreakpointName: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetName(self) -> str: ... + def SetEnabled(self, enable: bool) -> None: ... + def IsEnabled(self) -> bool: ... + def SetOneShot(self, one_shot: bool) -> None: ... + def IsOneShot(self) -> bool: ... + def SetIgnoreCount(self, count: int) -> None: ... + def GetIgnoreCount(self) -> int: ... + def SetCondition(self, condition: str) -> None: ... + def GetCondition(self) -> str: ... + def SetAutoContinue(self, auto_continue: bool) -> None: ... + def GetAutoContinue(self) -> bool: ... + def SetThreadID(self, sb_thread_id: int) -> None: ... + def GetThreadID(self) -> int: ... + def SetThreadIndex(self, index: int) -> None: ... + def GetThreadIndex(self) -> int: ... + def SetThreadName(self, thread_name: str) -> None: ... + def GetThreadName(self) -> str: ... + def SetQueueName(self, queue_name: str) -> None: ... + def GetQueueName(self) -> str: ... + def SetScriptCallbackFunction( + self, callback_function_name: str, extra_args: Union[SBStructuredData, None] = None + ) -> Union[SBError, None]: ... + def SetCommandLineCommands(self, commands: SBStringList) -> None: ... + def GetCommandLineCommands(self, commands: SBStringList) -> bool: ... + def SetScriptCallbackBody(self, script_body_text: str) -> SBError: ... + def GetHelpString(self) -> str: ... + def SetHelpString(self, help_string: str) -> None: ... + def GetAllowList(self) -> bool: ... + def SetAllowList(self, value: bool) -> None: ... + def GetAllowDelete(self) -> bool: ... + def SetAllowDelete(self, value: bool) -> None: ... + def GetAllowDisable(self) -> bool: ... + def SetAllowDisable(self, value: bool) -> None: ... + def GetDescription(self, description: SBStream) -> bool: ... + +class SBBroadcaster: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def Clear(self) -> None: ... + def BroadcastEventByType(self, event_type: int, unique: bool = False) -> None: ... + def BroadcastEvent(self, event: SBEvent, unique: bool = False) -> None: ... + def AddInitialEventsToListener(self, listener: SBListener, requested_events: int) -> None: ... + def AddListener(self, listener: SBListener, event_mask: int) -> int: ... + def GetName(self) -> str: ... + def EventTypeHasListeners(self, event_type: int) -> bool: ... + def RemoveListener(self, listener: SBListener, event_mask: int = 4294967295) -> bool: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def __lt__(self, rhs: Incomplete) -> bool: ... + +class SBCommandInterpreter: + thisown: Incomplete + eBroadcastBitThreadShouldExit: int + eBroadcastBitResetPrompt: int + eBroadcastBitQuitCommandReceived: int + eBroadcastBitAsynchronousOutputData: int + eBroadcastBitAsynchronousErrorData: int + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + @staticmethod + def GetArgumentTypeAsCString(arg_type: int) -> str: ... + @staticmethod + def GetArgumentDescriptionAsCString(arg_type: int) -> str: ... + @staticmethod + def EventIsCommandInterpreterEvent(event: SBEvent) -> bool: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def CommandExists(self, cmd: str) -> bool: ... + def UserCommandExists(self, cmd: str) -> bool: ... + def AliasExists(self, cmd: str) -> bool: ... + def GetBroadcaster(self) -> SBBroadcaster: ... + @staticmethod + def GetBroadcasterClass() -> str: ... + def HasCommands(self) -> bool: ... + def HasAliases(self) -> bool: ... + def HasAliasOptions(self) -> bool: ... + def IsInteractive(self) -> bool: ... + def GetProcess(self) -> SBProcess: ... + def GetDebugger(self) -> SBDebugger: ... + def SourceInitFileInHomeDirectory( + self, result: SBCommandReturnObject, is_repl: Union[bool, None] = None + ) -> None: ... + def SourceInitFileInCurrentWorkingDirectory(self, result: SBCommandReturnObject) -> None: ... + def HandleCommand(self, command_line: str, *args: Incomplete) -> int: ... + def HandleCommandsFromFile( + self, + file: SBFileSpec, + override_context: SBExecutionContext, + options: SBCommandInterpreterRunOptions, + result: SBCommandReturnObject, + ) -> None: ... + def HandleCompletion( + self, + current_line: str, + cursor_pos: int, + match_start_point: int, + max_return_elements: int, + matches: SBStringList, + ) -> int: ... + def HandleCompletionWithDescriptions( + self, + current_line: str, + cursor_pos: int, + match_start_point: int, + max_return_elements: int, + matches: SBStringList, + descriptions: SBStringList, + ) -> int: ... + def WasInterrupted(self) -> bool: ... + def InterruptCommand(self) -> bool: ... + def SetCommandOverrideCallback(self, command_name: str, callback: Callable[..., Incomplete]) -> None: ... + def IsActive(self) -> bool: ... + def GetIOHandlerControlSequence(self, ch: Incomplete) -> str: ... + def GetPromptOnQuit(self) -> bool: ... + def SetPromptOnQuit(self, b: bool) -> None: ... + def AllowExitCodeOnQuit(self, allow: bool) -> None: ... + def HasCustomQuitExitCode(self) -> bool: ... + def GetQuitStatus(self) -> int: ... + def ResolveCommand(self, command_line: str, result: SBCommandReturnObject) -> None: ... + def GetStatistics(self) -> SBStructuredData: ... + def GetTranscript(self) -> SBStructuredData: ... + +class SBCommandInterpreterRunOptions: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetStopOnContinue(self) -> bool: ... + def SetStopOnContinue(self, arg2: bool) -> None: ... + def GetStopOnError(self) -> bool: ... + def SetStopOnError(self, arg2: bool) -> None: ... + def GetStopOnCrash(self) -> bool: ... + def SetStopOnCrash(self, arg2: bool) -> None: ... + def GetEchoCommands(self) -> bool: ... + def SetEchoCommands(self, arg2: bool) -> None: ... + def GetEchoCommentCommands(self) -> bool: ... + def SetEchoCommentCommands(self, echo: bool) -> None: ... + def GetPrintResults(self) -> bool: ... + def SetPrintResults(self, arg2: bool) -> None: ... + def GetPrintErrors(self) -> bool: ... + def SetPrintErrors(self, arg2: bool) -> None: ... + def GetAddToHistory(self) -> bool: ... + def SetAddToHistory(self, arg2: bool) -> None: ... + def GetAutoHandleEvents(self) -> bool: ... + def SetAutoHandleEvents(self, arg2: bool) -> None: ... + def GetSpawnThread(self) -> bool: ... + def SetSpawnThread(self, arg2: bool) -> None: ... + def GetAllowRepeats(self) -> bool: ... + def SetAllowRepeats(self, arg2: bool) -> None: ... + +class SBCommandReturnObject: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetErrorData(self) -> SBStructuredData: ... + def PutOutput(self, file: Incomplete) -> int: ... + def GetOutputSize(self) -> int: ... + def GetErrorSize(self) -> int: ... + def PutError(self, file: Incomplete) -> int: ... + def Clear(self) -> None: ... + def GetStatus(self) -> int: ... + def SetStatus(self, status: int) -> None: ... + def Succeeded(self) -> bool: ... + def HasResult(self) -> bool: ... + def AppendWarning(self, message: str) -> None: ... + def AppendMessage(self, message: str) -> None: ... + def GetDescription(self, description: SBStream) -> bool: ... + def PutCString(self, string: str) -> None: ... + def GetOutput(self, only_if_no_immediate: bool = False) -> str: ... + def GetError(self, only_if_no_immediate: bool = False) -> str: ... + def SetError(self, *args: Incomplete) -> None: ... + def SetImmediateOutputFile(self, *args: Incomplete) -> None: ... + def SetImmediateErrorFile(self, *args: Incomplete) -> None: ... + def Print(self, str: str) -> None: ... + def write(self, str: str) -> None: ... + def flush(self) -> None: ... + +class SBCommunication: + thisown: Incomplete + eBroadcastBitDisconnected: int + eBroadcastBitReadThreadGotBytes: int + eBroadcastBitReadThreadDidExit: int + eBroadcastBitReadThreadShouldExit: int + eBroadcastBitPacketAvailable: int + eAllEventBits: int + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetBroadcaster(self) -> SBBroadcaster: ... + @staticmethod + def GetBroadcasterClass() -> str: ... + def AdoptFileDesriptor(self, fd: int, owns_fd: bool) -> int: ... + def Connect(self, url: int) -> int: ... + def Disconnect(self) -> int: ... + def IsConnected(self) -> bool: ... + def GetCloseOnEOF(self) -> bool: ... + def SetCloseOnEOF(self, b: bool) -> None: ... + def Read(self, *args: Incomplete) -> int: ... + def Write(self, *args: Incomplete) -> int: ... + def ReadThreadStart(self) -> bool: ... + def ReadThreadStop(self) -> bool: ... + def ReadThreadIsRunning(self) -> bool: ... + def SetReadThreadBytesReceivedCallback(self, *args: Incomplete) -> bool: ... + +class SBCompileUnit: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetFileSpec(self) -> SBFileSpec: ... + def GetNumLineEntries(self) -> int: ... + def GetLineEntryAtIndex(self, idx: int) -> SBLineEntry: ... + def FindLineEntryIndex(self, *args: Incomplete) -> int: ... + def GetSupportFileAtIndex(self, idx: int) -> SBFileSpec: ... + def GetNumSupportFiles(self) -> int: ... + def FindSupportFileIndex(self, start_idx: int, sb_file: SBFileSpec, full: bool) -> int: ... + def GetTypes(self, type_mask: int) -> SBTypeList: ... + def GetLanguage(self) -> int: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def GetDescription(self, description: SBStream) -> bool: ... + def __iter__(self) -> Iterator[SBLineEntry]: ... + def __len__(self) -> int: ... + file: SBFileSpec + num_line_entries: int + +class SBSaveCoreOptions: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def SetPluginName(self, plugin: Union[str, None] = None) -> SBError: ... + def GetPluginName(self) -> Union[str, None]: ... + def SetStyle(self, style: int) -> None: ... + def GetStyle(self) -> int: ... + def SetOutputFile(self, output_file: SBFileSpec) -> None: ... + def GetOutputFile(self) -> Union[SBFileSpec, None]: ... + def SetProcess(self, process: SBProcess) -> SBError: ... + def AddThread(self, thread: SBThread) -> SBError: ... + def RemoveThread(self, thread: SBThread) -> bool: ... + def AddMemoryRegionToSave(self, region: SBMemoryRegionInfo) -> SBError: ... + def GetThreadsToSave(self) -> SBThreadCollection: ... + def Clear(self) -> None: ... + +class SBData: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetAddressByteSize(self) -> int: ... + def SetAddressByteSize(self, addr_byte_size: int) -> None: ... + def Clear(self) -> None: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetByteSize(self) -> int: ... + def GetByteOrder(self) -> int: ... + def SetByteOrder(self, endian: int) -> None: ... + def GetFloat(self, error: SBError, offset: int) -> float: ... + def GetDouble(self, error: SBError, offset: int) -> float: ... + def GetLongDouble(self, error: SBError, offset: int) -> float: ... + def GetAddress(self, error: SBError, offset: int) -> int: ... + def GetUnsignedInt8(self, error: SBError, offset: int) -> int: ... + def GetUnsignedInt16(self, error: SBError, offset: int) -> int: ... + def GetUnsignedInt32(self, error: SBError, offset: int) -> int: ... + def GetUnsignedInt64(self, error: SBError, offset: int) -> int: ... + def GetSignedInt8(self, error: SBError, offset: int) -> int: ... + def GetSignedInt16(self, error: SBError, offset: int) -> int: ... + def GetSignedInt32(self, error: SBError, offset: int) -> int: ... + def GetSignedInt64(self, error: SBError, offset: int) -> int: ... + def GetString(self, error: SBError, offset: int) -> str: ... + def ReadRawData(self, error: SBError, offset: int, num: int) -> Union[bytes, None]: ... + def GetDescription(self, description: SBStream, base_addr: Union[int, None] = None) -> bool: ... + def SetData(self, error: SBError, *args: Incomplete) -> None: ... + def SetDataWithOwnership(self, error: SBError, *args: Incomplete) -> None: ... + def Append(self, rhs: SBData) -> bool: ... + @staticmethod + def CreateDataFromCString(endian: int, addr_byte_size: int, data: str) -> SBData: ... + @staticmethod + def CreateDataFromUInt64Array(endian: int, addr_byte_size: int, array: list[Union[int, bool]]) -> SBData: ... + @staticmethod + def CreateDataFromUInt32Array(endian: int, addr_byte_size: int, array: list[Union[int, bool]]) -> SBData: ... + @staticmethod + def CreateDataFromSInt64Array(endian: int, addr_byte_size: int, array: list[Union[int, bool]]) -> SBData: ... + @staticmethod + def CreateDataFromSInt32Array(endian: int, addr_byte_size: int, array: list[Union[int, bool]]) -> SBData: ... + @staticmethod + def CreateDataFromDoubleArray(endian: int, addr_byte_size: int, array: list[float]) -> SBData: ... + def SetDataFromCString(self, data: str) -> bool: ... + def SetDataFromUInt64Array(self, array: list[int]) -> bool: ... + def SetDataFromUInt32Array(self, array: list[int]) -> bool: ... + def SetDataFromSInt64Array(self, array: list[int]) -> bool: ... + def SetDataFromSInt32Array(self, array: list[int]) -> bool: ... + def SetDataFromDoubleArray(self, array: list[float]) -> bool: ... + def __len__(self) -> int: ... + + class read_data_helper: + sbdata: SBData + readerfunc: Incomplete + item_size: Incomplete + def __init__(self, sbdata: SBData, readerfunc: Incomplete, item_size: Incomplete) -> None: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + def __len__(self) -> int: ... + def all(self) -> Incomplete: ... + + @classmethod + def CreateDataFromInt(cls, *args: Incomplete) -> Incomplete: ... + uint8: Incomplete + uint16: Incomplete + uint32: Incomplete + uint64: Incomplete + sint8: Incomplete + sint16: Incomplete + sint32: Incomplete + sint64: Incomplete + float: Incomplete + double: Incomplete + uint8s: list[int] + uint16s: list[int] + uint32s: list[int] + uint64s: list[int] + sint8s: list[int] + sint16s: list[int] + sint32s: list[int] + sint64s: list[int] + floats: list[float] + doubles: list[float] + byte_order: int + size: int + +class SBDebugger: + thisown: Incomplete + eBroadcastBitProgress: int + eBroadcastBitWarning: int + eBroadcastBitError: int + eBroadcastBitProgressCategory: int + eBroadcastBitExternalProgress: int + eBroadcastBitExternalProgressCategory: int + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + @staticmethod + def GetBroadcasterClass() -> str: ... + @staticmethod + def SupportsLanguage(language: int) -> bool: ... + def GetBroadcaster(self) -> SBBroadcaster: ... + @staticmethod + def GetProgressFromEvent(event: SBEvent) -> str: ... + @staticmethod + def GetProgressDataFromEvent(event: SBEvent) -> SBStructuredData: ... + @staticmethod + def GetDiagnosticFromEvent(event: SBEvent) -> SBStructuredData: ... + @staticmethod + def Initialize() -> None: ... + @staticmethod + def InitializeWithErrorHandling() -> SBError: ... + @staticmethod + def PrintStackTraceOnError() -> None: ... + @staticmethod + def PrintDiagnosticsOnError() -> None: ... + @staticmethod + def Terminate() -> None: ... + @staticmethod + def Create(source_init_files: bool = False, log_callback: Incomplete = None) -> SBDebugger: ... + @staticmethod + def Destroy(debugger: SBDebugger) -> None: ... + @staticmethod + def MemoryPressureDetected() -> None: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def Clear(self) -> None: ... + def GetSetting(self, setting: Union[str, None] = None) -> SBStructuredData: ... + def SetAsync(self, b: bool) -> None: ... + def GetAsync(self) -> bool: ... + def SkipLLDBInitFiles(self, b: bool) -> None: ... + def SkipAppInitFiles(self, b: bool) -> None: ... + def SetInputString(self, data: str) -> SBError: ... + def SetInputFile(self, file: Incomplete) -> SBError: ... + def SetOutputFile(self, file: Incomplete) -> SBError: ... + def SetErrorFile(self, file: Incomplete) -> SBError: ... + def GetInputFile(self) -> SBFile: ... + def GetOutputFile(self) -> SBFile: ... + def GetErrorFile(self) -> SBFile: ... + def SaveInputTerminalState(self) -> None: ... + def RestoreInputTerminalState(self) -> None: ... + def GetCommandInterpreter(self) -> SBCommandInterpreter: ... + def HandleCommand(self, command: str) -> None: ... + def RequestInterrupt(self) -> None: ... + def CancelInterruptRequest(self) -> None: ... + def InterruptRequested(self) -> bool: ... + def GetListener(self) -> SBListener: ... + def HandleProcessEvent(self, process: SBProcess, event: SBEvent, *args: Incomplete) -> None: ... + def CreateTargetWithFileAndTargetTriple(self, filename: str, target_triple: str) -> SBTarget: ... + def CreateTargetWithFileAndArch(self, filename: str, archname: str) -> SBTarget: ... + def CreateTarget( + self, + filename: str, + target_triple: Union[str, None] = None, + platform_name: Union[str, None] = None, + add_dependent_modules: Union[bool, None] = None, + error: Union[SBError, None] = None, + ) -> SBTarget: ... + def GetDummyTarget(self) -> SBTarget: ... + def DeleteTarget(self, target: SBTarget) -> bool: ... + def GetTargetAtIndex(self, idx: int) -> SBTarget: ... + def GetIndexOfTarget(self, target: SBTarget) -> int: ... + def FindTargetWithProcessID(self, pid: int) -> SBTarget: ... + def FindTargetWithFileAndArch(self, filename: str, arch: str) -> SBTarget: ... + def GetNumTargets(self) -> int: ... + def GetSelectedTarget(self) -> SBTarget: ... + def SetSelectedTarget(self, target: SBTarget) -> None: ... + def GetSelectedPlatform(self) -> SBPlatform: ... + def SetSelectedPlatform(self, platform: SBPlatform) -> None: ... + def GetNumPlatforms(self) -> int: ... + def GetPlatformAtIndex(self, idx: int) -> SBPlatform: ... + def GetNumAvailablePlatforms(self) -> int: ... + def GetAvailablePlatformInfoAtIndex(self, idx: int) -> SBStructuredData: ... + def GetSourceManager(self) -> SBSourceManager: ... + def SetCurrentPlatform(self, platform_name: str) -> SBError: ... + def SetCurrentPlatformSDKRoot(self, sysroot: str) -> bool: ... + def SetUseExternalEditor(self, input: bool) -> bool: ... + def GetUseExternalEditor(self) -> bool: ... + def SetUseColor(self, use_color: bool) -> bool: ... + def GetUseColor(self) -> bool: ... + def SetShowInlineDiagnostics(self, arg2: bool) -> bool: ... + def SetUseSourceCache(self, use_source_cache: bool) -> bool: ... + def GetUseSourceCache(self) -> bool: ... + @staticmethod + def GetDefaultArchitecture(arch_name: str, arch_name_len: int) -> bool: ... + @staticmethod + def SetDefaultArchitecture(arch_name: str) -> bool: ... + def GetScriptingLanguage(self, script_language_name: str) -> int: ... + def GetScriptInterpreterInfo(self, arg2: int) -> SBStructuredData: ... + @staticmethod + def GetVersionString() -> str: ... + @staticmethod + def StateAsCString(state: int) -> str: ... + @staticmethod + def GetBuildConfiguration() -> SBStructuredData: ... + @staticmethod + def StateIsRunningState(state: int) -> bool: ... + @staticmethod + def StateIsStoppedState(state: int) -> bool: ... + def EnableLog(self, channel: str, categories: Incomplete) -> bool: ... + def SetLoggingCallback(self, log_callback: Incomplete) -> None: ... + def SetDestroyCallback(self, destroy_callback: Incomplete) -> None: ... + def AddDestroyCallback(self, destroy_callback: Incomplete) -> int: ... + def RemoveDestroyCallback(self, token: int) -> bool: ... + def DispatchInput(self, data: Incomplete) -> None: ... + def DispatchInputInterrupt(self) -> None: ... + def DispatchInputEndOfFile(self) -> None: ... + def GetInstanceName(self) -> str: ... + @staticmethod + def FindDebuggerWithID(id: int) -> SBDebugger: ... + @staticmethod + def SetInternalVariable(var_name: str, value: str, debugger_instance_name: str) -> SBError: ... + @staticmethod + def GetInternalVariableValue(var_name: str, debugger_instance_name: str) -> SBStringList: ... + def GetDescription(self, description: SBStream) -> bool: ... + def GetTerminalWidth(self) -> int: ... + def SetTerminalWidth(self, term_width: int) -> None: ... + def GetTerminalHeight(self) -> int: ... + def SetTerminalHeight(self, term_height: int) -> None: ... + def GetID(self) -> int: ... + def GetPrompt(self) -> str: ... + def SetPrompt(self, prompt: str) -> None: ... + def GetReproducerPath(self) -> str: ... + def GetScriptLanguage(self) -> int: ... + def SetScriptLanguage(self, script_lang: int) -> None: ... + def GetREPLLanguage(self) -> int: ... + def SetREPLLanguage(self, repl_lang: int) -> None: ... + def GetCloseInputOnEOF(self) -> bool: ... + def SetCloseInputOnEOF(self, b: bool) -> None: ... + def GetCategory(self, *args: Incomplete) -> SBTypeCategory: ... + def CreateCategory(self, category_name: str) -> SBTypeCategory: ... + def DeleteCategory(self, category_name: str) -> bool: ... + def GetNumCategories(self) -> int: ... + def GetCategoryAtIndex(self, arg2: int) -> SBTypeCategory: ... + def GetDefaultCategory(self) -> SBTypeCategory: ... + def GetFormatForType(self, arg2: SBTypeNameSpecifier) -> SBTypeFormat: ... + def GetSummaryForType(self, arg2: SBTypeNameSpecifier) -> SBTypeSummary: ... + def GetFilterForType(self, arg2: SBTypeNameSpecifier) -> SBTypeFilter: ... + def GetSyntheticForType(self, arg2: SBTypeNameSpecifier) -> SBTypeSynthetic: ... + def ResetStatistics(self) -> None: ... + def RunCommandInterpreter( + self, + auto_handle_events: bool, + spawn_thread: bool, + options: SBCommandInterpreterRunOptions, + num_errors: Incomplete, + quit_requested: Incomplete, + stopped_for_crash: Incomplete, + ) -> None: ... + def RunREPL(self, language: int, repl_options: str) -> SBError: ... + def LoadTraceFromFile(self, error: SBError, trace_description_file: SBFileSpec) -> SBTrace: ... + def SetOutputFileHandle(self, file: Incomplete, transfer_ownership: Incomplete) -> None: ... + def SetInputFileHandle(self, file: Incomplete, transfer_ownership: Incomplete) -> None: ... + def SetErrorFileHandle(self, file: Incomplete, transfer_ownership: Incomplete) -> None: ... + def __iter__(self) -> Iterator[Incomplete]: ... + def __len__(self) -> int: ... + def GetInputFileHandle(self) -> Incomplete: ... + def GetOutputFileHandle(self) -> Incomplete: ... + def GetErrorFileHandle(self) -> Incomplete: ... + +class SBDeclaration: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetFileSpec(self) -> SBFileSpec: ... + def GetLine(self) -> int: ... + def GetColumn(self) -> int: ... + def SetFileSpec(self, filespec: SBFileSpec) -> None: ... + def SetLine(self, line: int) -> None: ... + def SetColumn(self, column: int) -> None: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def GetDescription(self, description: SBStream) -> bool: ... + file: SBFileSpec + line: int + column: int + +class SBError: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetCString(self) -> str: ... + def Clear(self) -> None: ... + def Fail(self) -> bool: ... + def Success(self) -> bool: ... + def GetError(self) -> int: ... + def GetErrorData(self) -> SBStructuredData: ... + def GetType(self) -> int: ... + def SetError(self, err: int, type: int) -> None: ... + def SetErrorToErrno(self) -> None: ... + def SetErrorToGenericError(self) -> None: ... + def SetErrorString(self, err_str: str) -> None: ... + def SetErrorStringWithFormat( + self, + format: str, + str1: Union[str, None] = None, + str2: Union[str, None] = None, + str3: Union[str, None] = None, + ) -> int: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetDescription(self, description: SBStream) -> bool: ... + def __int__(self) -> int: ... + value: int + fail: bool + success: bool + description: str + type: int + +class SBEnvironment: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def Get(self, name: str) -> str: ... + def GetNumValues(self) -> int: ... + def GetNameAtIndex(self, index: int) -> str: ... + def GetValueAtIndex(self, index: int) -> str: ... + def GetEntries(self) -> SBStringList: ... + def PutEntry(self, name_and_value: str) -> None: ... + def SetEntries(self, entries: SBStringList, append: bool) -> None: ... + def Set(self, name: str, value: str, overwrite: bool) -> bool: ... + def Unset(self, name: str) -> bool: ... + def Clear(self) -> None: ... + +class SBEvent: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetDataFlavor(self) -> str: ... + def GetType(self) -> int: ... + def GetBroadcaster(self) -> SBBroadcaster: ... + def GetBroadcasterClass(self) -> str: ... + def BroadcasterMatchesRef(self, broadcaster: SBBroadcaster) -> bool: ... + def Clear(self) -> None: ... + @staticmethod + def GetCStringFromEvent(event: SBEvent) -> str: ... + def GetDescription(self, description: SBStream) -> bool: ... + +class SBExecutionContext: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetTarget(self) -> SBTarget: ... + def GetProcess(self) -> SBProcess: ... + def GetThread(self) -> SBThread: ... + def GetFrame(self) -> SBFrame: ... + target: SBTarget + process: SBProcess + thread: SBThread + frame: SBFrame + +class SBExpressionOptions: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetCoerceResultToId(self) -> bool: ... + def SetCoerceResultToId(self, coerce: bool = True) -> None: ... + def GetUnwindOnError(self) -> bool: ... + def SetUnwindOnError(self, unwind: bool = True) -> None: ... + def GetIgnoreBreakpoints(self) -> bool: ... + def SetIgnoreBreakpoints(self, ignore: bool = True) -> None: ... + def GetFetchDynamicValue(self) -> int: ... + def SetFetchDynamicValue(self, dynamic: int) -> None: ... + def GetTimeoutInMicroSeconds(self) -> int: ... + def SetTimeoutInMicroSeconds(self, timeout: int = 0) -> None: ... + def GetOneThreadTimeoutInMicroSeconds(self) -> int: ... + def SetOneThreadTimeoutInMicroSeconds(self, timeout: int = 0) -> None: ... + def GetTryAllThreads(self) -> bool: ... + def SetTryAllThreads(self, run_others: bool = True) -> None: ... + def GetStopOthers(self) -> bool: ... + def SetStopOthers(self, stop_others: bool = True) -> None: ... + def GetTrapExceptions(self) -> bool: ... + def SetTrapExceptions(self, trap_exceptions: bool = True) -> None: ... + def SetLanguage(self, *args: Incomplete) -> None: ... + def GetGenerateDebugInfo(self) -> bool: ... + def SetGenerateDebugInfo(self, b: bool = True) -> None: ... + def GetSuppressPersistentResult(self) -> bool: ... + def SetSuppressPersistentResult(self, b: bool = False) -> None: ... + def GetPrefix(self) -> str: ... + def SetPrefix(self, prefix: str) -> None: ... + def SetAutoApplyFixIts(self, b: bool = True) -> None: ... + def GetAutoApplyFixIts(self) -> bool: ... + def SetRetriesWithFixIts(self, retries: int) -> None: ... + def GetRetriesWithFixIts(self) -> int: ... + def GetTopLevel(self) -> bool: ... + def SetTopLevel(self, b: bool = True) -> None: ... + def GetAllowJIT(self) -> bool: ... + def SetAllowJIT(self, allow: bool) -> None: ... + +class SBFile: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def Read(self, buf: Incomplete) -> tuple[SBError, int]: ... + def Write(self, buf: Incomplete) -> tuple[SBError, int]: ... + def Flush(self) -> SBError: ... + def IsValid(self) -> bool: ... + def Close(self) -> SBError: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def GetFile(self) -> IOBase: ... + @staticmethod + def MakeBorrowed(BORROWED: IOBase) -> SBFile: ... + @staticmethod + def MakeForcingIOMethods(FORCE_IO_METHODS: IOBase) -> SBFile: ... + @staticmethod + def MakeBorrowedForcingIOMethods(BORROWED_FORCE_IO_METHODS: IOBase) -> SBFile: ... + @classmethod + def Create(cls, file: Incomplete, borrow: bool = False, force_io_methods: bool = False) -> SBFile: ... + +class SBFileSpec: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def IsValid(self) -> bool: ... + def Exists(self) -> bool: ... + def ResolveExecutableLocation(self) -> bool: ... + def GetFilename(self) -> str: ... + def GetDirectory(self) -> str: ... + def SetFilename(self, filename: str) -> None: ... + def SetDirectory(self, directory: str) -> None: ... + def GetPath(self, dst_path: Incomplete, dst_len: int) -> int: ... + @staticmethod + def ResolvePath(src_path: Incomplete, dst_path: Incomplete, dst_len: int) -> int: ... + def GetDescription(self, description: SBStream) -> bool: ... + def AppendPathComponent(self, file_or_directory: str) -> None: ... + fullpath: str + basename: str + dirname: str + exists: bool + +class SBFileSpecList: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetSize(self) -> int: ... + def GetDescription(self, description: SBStream) -> bool: ... + def Append(self, sb_file: SBFileSpec) -> None: ... + def AppendIfUnique(self, sb_file: SBFileSpec) -> bool: ... + def Clear(self) -> None: ... + def FindFileIndex(self, idx: int, sb_file: SBFileSpec, full: bool) -> int: ... + def GetFileSpecAtIndex(self, idx: int) -> SBFileSpec: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[SBFileSpec]: ... + +class SBFormat: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + +class SBFrame: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def IsEqual(self, that: SBFrame) -> bool: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetFrameID(self) -> int: ... + def GetCFA(self) -> int: ... + def GetPC(self) -> int: ... + def SetPC(self, new_pc: int) -> bool: ... + def GetSP(self) -> int: ... + def GetFP(self) -> int: ... + def GetPCAddress(self) -> SBAddress: ... + def GetSymbolContext(self, resolve_scope: int) -> SBSymbolContext: ... + def GetModule(self) -> SBModule: ... + def GetCompileUnit(self) -> SBCompileUnit: ... + def GetFunction(self) -> SBFunction: ... + def GetSymbol(self) -> SBSymbol: ... + def GetBlock(self) -> SBBlock: ... + def GetDisplayFunctionName(self) -> str: ... + def GetFunctionName(self) -> Union[str, None]: ... + def GuessLanguage(self) -> int: ... + def IsInlined(self) -> bool: ... + def IsArtificial(self) -> bool: ... + def IsHidden(self) -> bool: ... + def EvaluateExpression(self, *args: Incomplete) -> SBValue: ... + def GetLanguageSpecificData(self) -> SBStructuredData: ... + def GetFrameBlock(self) -> SBBlock: ... + def GetLineEntry(self) -> SBLineEntry: ... + def GetThread(self) -> SBThread: ... + def Disassemble(self) -> str: ... + def Clear(self) -> None: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def GetVariables(self, *args: Incomplete) -> SBValueList: ... + def GetRegisters(self) -> SBValueList: ... + def FindRegister(self, name: str) -> SBValue: ... + def FindVariable(self, name: str, use_dynamic: Union[int, None] = None) -> SBValue: ... + def GetValueForVariablePath(self, var: str, use_dynamic: Union[int, None] = None) -> SBValue: ... + def FindValue(self, name: str, value_type: int, use_dynamic: Union[int, None] = None) -> SBValue: ... + def GetDescription(self, description: SBStream) -> bool: ... + def GetDescriptionWithFormat(self, format: SBFormat, output: SBStream) -> SBError: ... + def __int__(self) -> int: ... + def __hex__(self) -> Incomplete: ... + def get_all_variables(self) -> Incomplete: ... + def get_parent_frame(self) -> Incomplete: ... + def get_arguments(self) -> Incomplete: ... + def get_locals(self) -> Incomplete: ... + def get_statics(self) -> Incomplete: ... + def var(self, var_expr_path: Incomplete) -> Incomplete: ... + regs: list[Incomplete] + def get_registers_access(self) -> Incomplete: ... + pc: int + addr: SBAddress + fp: int + sp: int + module: SBModule + compile_unit: SBCompileUnit + function: SBFunction + symbol: SBSymbol + block: SBBlock + is_inlined: bool + name: str + line_entry: SBLineEntry + thread: SBThread + disassembly: str + idx: int + variables: list[SBValue] + vars: list[SBValue] + locals: list[SBValue] + args: list[SBValue] + arguments: list[SBValue] + statics: list[SBValue] + registers: list[Incomplete] + register: Incomplete + reg: Incomplete + parent: Incomplete + +class SBFunction: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetName(self) -> str: ... + def GetDisplayName(self) -> str: ... + def GetMangledName(self) -> str: ... + def GetInstructions(self, target: SBTarget, flavor: Union[str, None] = None) -> SBInstructionList: ... + def GetStartAddress(self) -> SBAddress: ... + def GetEndAddress(self) -> SBAddress: ... + def GetRanges(self) -> SBAddressRangeList: ... + def GetArgumentName(self, arg_idx: int) -> str: ... + def GetPrologueByteSize(self) -> int: ... + def GetType(self) -> SBType: ... + def GetBlock(self) -> SBBlock: ... + def GetLanguage(self) -> int: ... + def GetIsOptimized(self) -> bool: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def GetDescription(self, description: SBStream) -> bool: ... + def get_instructions_from_current_target(self) -> Incomplete: ... + addr: SBAddress + end_addr: SBAddress + block: SBBlock + instructions: SBInstructionList + mangled: str + name: str + prologue_size: int + type: SBType + +class SBHostOS: + thisown: Incomplete + @staticmethod + def GetProgramFileSpec() -> SBFileSpec: ... + @staticmethod + def GetLLDBPythonPath() -> SBFileSpec: ... + @staticmethod + def GetLLDBPath(path_type: int) -> SBFileSpec: ... + @staticmethod + def GetUserHomeDirectory() -> SBFileSpec: ... + @staticmethod + def ThreadCreated(name: str) -> None: ... + @staticmethod + def ThreadCreate(name: str, thread_function: Incomplete, thread_arg: Incomplete, err: SBError) -> Incomplete: ... + @staticmethod + def ThreadCancel(thread: Incomplete, err: SBError) -> bool: ... + @staticmethod + def ThreadDetach(thread: Incomplete, err: SBError) -> bool: ... + @staticmethod + def ThreadJoin(thread: Incomplete, result: Incomplete, err: SBError) -> bool: ... + def __init__(self) -> None: ... + __swig_destroy__: Incomplete + +class SBInstruction: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetAddress(self) -> SBAddress: ... + def GetMnemonic(self, target: SBTarget) -> str: ... + def GetOperands(self, target: SBTarget) -> str: ... + def GetComment(self, target: SBTarget) -> str: ... + def GetControlFlowKind(self, target: SBTarget) -> int: ... + def GetData(self, target: SBTarget) -> SBData: ... + def GetByteSize(self) -> int: ... + def DoesBranch(self) -> bool: ... + def HasDelaySlot(self) -> bool: ... + def CanSetBreakpoint(self) -> bool: ... + def Print(self, out: Incomplete) -> None: ... + def GetDescription(self, description: SBStream) -> bool: ... + def EmulateWithFrame(self, frame: SBFrame, evaluate_options: int) -> bool: ... + def DumpEmulation(self, triple: str) -> bool: ... + def TestEmulation(self, output_stream: SBStream, test_file: str) -> bool: ... + def __hex__(self) -> Incomplete: ... + def __len__(self) -> int: ... + def __mnemonic_property__(self) -> Incomplete: ... + def __operands_property__(self) -> Incomplete: ... + def __comment_property__(self) -> Incomplete: ... + def __file_addr_property__(self) -> Incomplete: ... + def __load_adrr_property__(self) -> Incomplete: ... + mnemonic: str + operands: str + comment: str + addr: SBAddress + size: int + is_branch: bool + +class SBInstructionList: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetSize(self) -> int: ... + def GetInstructionAtIndex(self, idx: int) -> SBInstruction: ... + def GetInstructionsCount(self, start: SBAddress, end: SBAddress, canSetBreakpoint: bool = False) -> int: ... + def Clear(self) -> None: ... + def AppendInstruction(self, inst: SBInstruction) -> None: ... + def Print(self, out: Incomplete) -> None: ... + def GetDescription(self, description: SBStream, exe_ctx: Union[SBExecutionContext, None] = None) -> bool: ... + def DumpEmulationForAllInstructions(self, triple: str) -> bool: ... + def __iter__(self) -> Iterator[SBInstruction]: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + +eLanguageNameAda: int +eLanguageNameBLISS: int +eLanguageNameC: int +eLanguageNameC_plus_plus: int +eLanguageNameCobol: int +eLanguageNameCrystal: int +eLanguageNameD: int +eLanguageNameDylan: int +eLanguageNameFortran: int +eLanguageNameGo: int +eLanguageNameHaskell: int +eLanguageNameJava: int +eLanguageNameJulia: int +eLanguageNameKotlin: int +eLanguageNameModula2: int +eLanguageNameModula3: int +eLanguageNameObjC: int +eLanguageNameObjC_plus_plus: int +eLanguageNameOCaml: int +eLanguageNameOpenCL_C: int +eLanguageNamePascal: int +eLanguageNamePLI: int +eLanguageNamePython: int +eLanguageNameRenderScript: int +eLanguageNameRust: int +eLanguageNameSwift: int +eLanguageNameUPC: int +eLanguageNameZig: int +eLanguageNameAssembly: int +eLanguageNameC_sharp: int +eLanguageNameMojo: int +eLanguageNameGLSL: int +eLanguageNameGLSL_ES: int +eLanguageNameHLSL: int +eLanguageNameOpenCL_CPP: int +eLanguageNameCPP_for_OpenCL: int +eLanguageNameSYCL: int +eLanguageNameRuby: int +eLanguageNameMove: int +eLanguageNameHylo: int +eLanguageNameMetal: int + +class SBLanguageRuntime: + thisown: Incomplete + @staticmethod + def GetLanguageTypeFromString(string: str) -> int: ... + @staticmethod + def GetNameForLanguageType(language: int) -> str: ... + @staticmethod + def LanguageIsCPlusPlus(language: int) -> bool: ... + @staticmethod + def LanguageIsObjC(language: int) -> bool: ... + @staticmethod + def LanguageIsCFamily(language: int) -> bool: ... + @staticmethod + def SupportsExceptionBreakpointsOnThrow(language: int) -> bool: ... + @staticmethod + def SupportsExceptionBreakpointsOnCatch(language: int) -> bool: ... + @staticmethod + def GetThrowKeywordForLanguage(language: int) -> str: ... + @staticmethod + def GetCatchKeywordForLanguage(language: int) -> str: ... + def __init__(self) -> None: ... + __swig_destroy__: Incomplete + +class SBLaunchInfo: + thisown: Incomplete + def __init__(self, argv: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetProcessID(self) -> int: ... + def GetUserID(self) -> int: ... + def GetGroupID(self) -> int: ... + def UserIDIsValid(self) -> bool: ... + def GroupIDIsValid(self) -> bool: ... + def SetUserID(self, uid: int) -> None: ... + def SetGroupID(self, gid: int) -> None: ... + def GetExecutableFile(self) -> SBFileSpec: ... + def SetExecutableFile(self, exe_file: SBFileSpec, add_as_first_arg: bool) -> None: ... + def GetListener(self) -> SBListener: ... + def SetListener(self, listener: SBListener) -> None: ... + def GetShadowListener(self) -> SBListener: ... + def SetShadowListener(self, listener: SBListener) -> None: ... + def GetNumArguments(self) -> int: ... + def GetArgumentAtIndex(self, idx: int) -> str: ... + def SetArguments(self, argv: Incomplete, append: bool) -> None: ... + def GetNumEnvironmentEntries(self) -> int: ... + def GetEnvironmentEntryAtIndex(self, idx: int) -> str: ... + def SetEnvironmentEntries(self, envp: Incomplete, append: bool) -> None: ... + def SetEnvironment(self, env: SBEnvironment, append: bool) -> None: ... + def GetEnvironment(self) -> SBEnvironment: ... + def Clear(self) -> None: ... + def GetWorkingDirectory(self) -> str: ... + def SetWorkingDirectory(self, working_dir: str) -> None: ... + def GetLaunchFlags(self) -> int: ... + def SetLaunchFlags(self, flags: int) -> None: ... + def GetProcessPluginName(self) -> str: ... + def SetProcessPluginName(self, plugin_name: str) -> None: ... + def GetShell(self) -> str: ... + def SetShell(self, path: str) -> None: ... + def GetShellExpandArguments(self) -> bool: ... + def SetShellExpandArguments(self, expand: bool) -> None: ... + def GetResumeCount(self) -> int: ... + def SetResumeCount(self, c: int) -> None: ... + def AddCloseFileAction(self, fd: int) -> bool: ... + def AddDuplicateFileAction(self, fd: int, dup_fd: int) -> bool: ... + def AddOpenFileAction(self, fd: int, path: str, read: bool, write: bool) -> bool: ... + def AddSuppressFileAction(self, fd: int, read: bool, write: bool) -> bool: ... + def SetLaunchEventData(self, data: str) -> None: ... + def GetLaunchEventData(self) -> str: ... + def GetDetachOnError(self) -> bool: ... + def SetDetachOnError(self, enable: bool) -> None: ... + def GetScriptedProcessClassName(self) -> str: ... + def SetScriptedProcessClassName(self, class_name: str) -> None: ... + def GetScriptedProcessDictionary(self) -> SBStructuredData: ... + def SetScriptedProcessDictionary(self, dict: SBStructuredData) -> None: ... + +class SBLineEntry: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetStartAddress(self) -> SBAddress: ... + def GetEndAddress(self) -> SBAddress: ... + def GetSameLineContiguousAddressRangeEnd(self, include_inlined_functions: bool) -> SBAddress: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetFileSpec(self) -> SBFileSpec: ... + def GetLine(self) -> int: ... + def GetColumn(self) -> int: ... + def SetFileSpec(self, filespec: SBFileSpec) -> None: ... + def SetLine(self, line: int) -> None: ... + def SetColumn(self, column: int) -> None: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def GetDescription(self, description: SBStream) -> bool: ... + def __int__(self) -> int: ... + def __hex__(self) -> Incomplete: ... + file: SBFileSpec + line: int + column: int + addr: SBAddress + end_addr: SBAddress + +class SBListener: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def AddEvent(self, event: SBEvent) -> None: ... + def Clear(self) -> None: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def StartListeningForEventClass(self, debugger: SBDebugger, broadcaster_class: str, event_mask: int) -> int: ... + def StopListeningForEventClass(self, debugger: SBDebugger, broadcaster_class: str, event_mask: int) -> bool: ... + def StartListeningForEvents(self, broadcaster: SBBroadcaster, event_mask: int) -> int: ... + def StopListeningForEvents(self, broadcaster: SBBroadcaster, event_mask: int) -> bool: ... + def WaitForEvent(self, num_seconds: int, event: SBEvent) -> bool: ... + def WaitForEventForBroadcaster(self, num_seconds: int, broadcaster: SBBroadcaster, sb_event: SBEvent) -> bool: ... + def WaitForEventForBroadcasterWithType( + self, + num_seconds: int, + broadcaster: SBBroadcaster, + event_type_mask: int, + sb_event: SBEvent, + ) -> bool: ... + def PeekAtNextEvent(self, sb_event: SBEvent) -> bool: ... + def PeekAtNextEventForBroadcaster(self, broadcaster: SBBroadcaster, sb_event: SBEvent) -> bool: ... + def PeekAtNextEventForBroadcasterWithType( + self, broadcaster: SBBroadcaster, event_type_mask: int, sb_event: SBEvent + ) -> bool: ... + def GetNextEvent(self, sb_event: SBEvent) -> bool: ... + def GetNextEventForBroadcaster(self, broadcaster: SBBroadcaster, sb_event: SBEvent) -> bool: ... + def GetNextEventForBroadcasterWithType( + self, broadcaster: SBBroadcaster, event_type_mask: int, sb_event: SBEvent + ) -> bool: ... + def HandleBroadcastEvent(self, event: SBEvent) -> bool: ... + +class SBMemoryRegionInfo: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def Clear(self) -> None: ... + def GetRegionBase(self) -> int: ... + def GetRegionEnd(self) -> int: ... + def IsReadable(self) -> bool: ... + def IsWritable(self) -> bool: ... + def IsExecutable(self) -> bool: ... + def IsMapped(self) -> bool: ... + def GetName(self) -> str: ... + def HasDirtyMemoryPageList(self) -> bool: ... + def GetNumDirtyPages(self) -> int: ... + def GetDirtyPageAddressAtIndex(self, idx: int) -> int: ... + def GetPageSize(self) -> int: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def GetDescription(self, description: SBStream) -> bool: ... + def __hex__(self) -> Incomplete: ... + def __len__(self) -> int: ... + +class SBMemoryRegionInfoList: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetSize(self) -> int: ... + def GetMemoryRegionContainingAddress(self, addr: int, region_info: SBMemoryRegionInfo) -> bool: ... + def GetMemoryRegionAtIndex(self, idx: int, region_info: SBMemoryRegionInfo) -> bool: ... + def Append(self, reg: Union[SBMemoryRegionInfo, SBMemoryRegionInfoList]) -> None: ... + def Clear(self) -> None: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[SBMemoryRegionInfo]: ... + +class SBModule: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def Clear(self) -> None: ... + def IsFileBacked(self) -> bool: ... + def GetFileSpec(self) -> SBFileSpec: ... + def GetPlatformFileSpec(self) -> SBFileSpec: ... + def SetPlatformFileSpec(self, platform_file: SBFileSpec) -> bool: ... + def GetRemoteInstallFileSpec(self) -> SBFileSpec: ... + def SetRemoteInstallFileSpec(self, file: SBFileSpec) -> bool: ... + def GetByteOrder(self) -> int: ... + def GetAddressByteSize(self) -> int: ... + def GetTriple(self) -> str: ... + def GetUUIDBytes(self) -> Incomplete: ... + def GetUUIDString(self) -> str: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def FindSection(self, sect_name: str) -> SBSection: ... + def ResolveFileAddress(self, vm_addr: int) -> SBAddress: ... + def ResolveSymbolContextForAddress(self, addr: SBAddress, resolve_scope: int) -> SBSymbolContext: ... + def GetDescription(self, description: SBStream) -> bool: ... + def GetNumCompileUnits(self) -> int: ... + def GetCompileUnitAtIndex(self, arg2: int) -> SBCompileUnit: ... + def FindCompileUnits(self, sb_file_spec: SBFileSpec) -> SBSymbolContextList: ... + def GetNumSymbols(self) -> int: ... + def GetSymbolAtIndex(self, idx: int) -> SBSymbol: ... + def FindSymbol(self, name: str, type: int) -> SBSymbol: ... + def FindSymbols(self, name: str, type: int) -> SBSymbolContextList: ... + def GetNumSections(self) -> int: ... + def GetSectionAtIndex(self, idx: int) -> SBSection: ... + def FindFunctions(self, name: str, name_type_mask: int) -> SBSymbolContextList: ... + def FindGlobalVariables(self, target: SBTarget, name: str, max_matches: int) -> SBValueList: ... + def FindFirstGlobalVariable(self, target: SBTarget, name: str) -> SBValue: ... + def FindFirstType(self, name: str) -> SBType: ... + def FindTypes(self, type: str) -> SBTypeList: ... + def GetTypeByID(self, uid: int) -> SBType: ... + def GetBasicType(self, type: int) -> SBType: ... + def GetTypes(self, type_mask: int) -> SBTypeList: ... + def GetVersion(self) -> int: ... + def GetSymbolFileSpec(self) -> SBFileSpec: ... + def GetObjectFileHeaderAddress(self) -> SBAddress: ... + def GetObjectFileEntryPointAddress(self) -> SBAddress: ... + @staticmethod + def GetNumberAllocatedModules() -> int: ... + @staticmethod + def GarbageCollectAllocatedModules() -> None: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[SBSymbol]: ... + def section_iter(self) -> Iterator[SBSection]: ... + def compile_unit_iter(self) -> Iterator[SBCompileUnit]: ... + def symbol_in_section_iter(self, section: SBSection) -> Iterator[SBSymbol]: ... + + class symbols_access: + re_compile_type: Incomplete + sbmodule: SBModule + def __init__(self, sbmodule: SBModule) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + + def get_symbols_access_object(self) -> symbols_access: ... + def get_compile_units_access_object(self) -> compile_units_access: ... + def get_symbols_array(self) -> list[Incomplete]: ... + + class sections_access: + re_compile_type: Incomplete + sbmodule: SBModule + def __init__(self, sbmodule: SBModule) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + + class compile_units_access: + re_compile_type: Incomplete + sbmodule: SBModule + def __init__(self, sbmodule: SBModule) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + + def get_sections_access_object(self) -> sections_access: ... + sections_array: Incomplete + def get_sections_array(self) -> Incomplete: ... + compile_units_array: Incomplete + def get_compile_units_array(self) -> Incomplete: ... + symbols: list[SBSymbol] + symbol: Incomplete + sections: list[SBSection] + compile_units: list[SBCompileUnit] + section: Incomplete + def get_uuid(self) -> UUID: ... + uuid: UUID + file: SBFileSpec + platform_file: SBFileSpec + byte_order: int + addr_size: int + triple: str + num_symbols: int + num_sections: int + +class SBModuleSpec: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def Clear(self) -> None: ... + def GetFileSpec(self) -> SBFileSpec: ... + def SetFileSpec(self, fspec: SBFileSpec) -> None: ... + def GetPlatformFileSpec(self) -> SBFileSpec: ... + def SetPlatformFileSpec(self, fspec: SBFileSpec) -> None: ... + def GetSymbolFileSpec(self) -> SBFileSpec: ... + def SetSymbolFileSpec(self, fspec: SBFileSpec) -> None: ... + def GetObjectName(self) -> str: ... + def SetObjectName(self, name: str) -> None: ... + def GetTriple(self) -> str: ... + def SetTriple(self, triple: str) -> None: ... + def GetUUIDBytes(self) -> Incomplete: ... + def GetUUIDLength(self) -> int: ... + def SetUUIDBytes(self, uuid: Incomplete, uuid_len: int) -> bool: ... + def GetObjectOffset(self) -> int: ... + def SetObjectOffset(self, object_offset: int) -> None: ... + def GetObjectSize(self) -> int: ... + def SetObjectSize(self, object_size: int) -> None: ... + def GetDescription(self, description: SBStream) -> bool: ... + +class SBModuleSpecList: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + @staticmethod + def GetModuleSpecifications(path: str) -> SBModuleSpecList: ... + def Append(self, spec: Union[SBModuleSpec, SBModuleSpecList]) -> None: ... + def FindFirstMatchingSpec(self, match_spec: SBModuleSpec) -> SBModuleSpec: ... + def FindMatchingSpecs(self, match_spec: SBModuleSpec) -> SBModuleSpecList: ... + def GetSize(self) -> int: ... + def GetSpecAtIndex(self, i: int) -> SBModuleSpec: ... + def GetDescription(self, description: SBStream) -> bool: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[SBModuleSpec]: ... + +class SBPlatformConnectOptions: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetURL(self) -> str: ... + def SetURL(self, url: str) -> None: ... + def GetRsyncEnabled(self) -> bool: ... + def EnableRsync(self, options: str, remote_path_prefix: str, omit_remote_hostname: bool) -> None: ... + def DisableRsync(self) -> None: ... + def GetLocalCacheDirectory(self) -> str: ... + def SetLocalCacheDirectory(self, path: str) -> None: ... + +class SBPlatformShellCommand: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def Clear(self) -> None: ... + def GetShell(self) -> str: ... + def SetShell(self, shell: str) -> None: ... + def GetCommand(self) -> str: ... + def SetCommand(self, shell_command: str) -> None: ... + def GetWorkingDirectory(self) -> str: ... + def SetWorkingDirectory(self, path: str) -> None: ... + def GetTimeoutSeconds(self) -> int: ... + def SetTimeoutSeconds(self, sec: int) -> None: ... + def GetSignal(self) -> int: ... + def GetStatus(self) -> int: ... + def GetOutput(self) -> str: ... + +class SBPlatform: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + @staticmethod + def GetHostPlatform() -> SBPlatform: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def Clear(self) -> None: ... + def GetWorkingDirectory(self) -> str: ... + def SetWorkingDirectory(self, path: str) -> bool: ... + def GetName(self) -> str: ... + def ConnectRemote(self, connect_options: SBPlatformConnectOptions) -> SBError: ... + def DisconnectRemote(self) -> None: ... + def IsConnected(self) -> bool: ... + def GetTriple(self) -> str: ... + def GetHostname(self) -> str: ... + def GetOSBuild(self) -> str: ... + def GetOSDescription(self) -> str: ... + def GetOSMajorVersion(self) -> int: ... + def GetOSMinorVersion(self) -> int: ... + def GetOSUpdateVersion(self) -> int: ... + def SetSDKRoot(self, sysroot: str) -> None: ... + def Put(self, src: SBFileSpec, dst: SBFileSpec) -> SBError: ... + def Get(self, src: SBFileSpec, dst: SBFileSpec) -> SBError: ... + def Install(self, src: SBFileSpec, dst: SBFileSpec) -> SBError: ... + def Run(self, shell_command: SBPlatformShellCommand) -> SBError: ... + def Launch(self, launch_info: SBLaunchInfo) -> SBError: ... + def Attach( + self, + attach_info: SBAttachInfo, + debugger: SBDebugger, + target: SBTarget, + error: SBError, + ) -> SBProcess: ... + def GetAllProcesses(self, error: SBError) -> SBProcessInfoList: ... + def Kill(self, pid: int) -> SBError: ... + def MakeDirectory(self, path: str, file_permissions: int) -> SBError: ... + def GetFilePermissions(self, path: str) -> int: ... + def SetFilePermissions(self, path: str, file_permissions: int) -> SBError: ... + def GetUnixSignals(self) -> SBUnixSignals: ... + def GetEnvironment(self) -> SBEnvironment: ... + def SetLocateModuleCallback(self, callback: Incomplete) -> SBError: ... + +class SBProcess: + thisown: Incomplete + eBroadcastBitStateChanged: int + eBroadcastBitInterrupt: int + eBroadcastBitSTDOUT: int + eBroadcastBitSTDERR: int + eBroadcastBitProfileData: int + eBroadcastBitStructuredData: int + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + @staticmethod + def GetBroadcasterClassName() -> str: ... + def GetPluginName(self) -> str: ... + def GetShortPluginName(self) -> str: ... + def Clear(self) -> None: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetTarget(self) -> SBTarget: ... + def GetByteOrder(self) -> int: ... + def PutSTDIN(self, src: str) -> int: ... + def GetSTDOUT(self, dst_size: int) -> str: ... + def GetSTDERR(self, dst_size: int) -> str: ... + def GetAsyncProfileData(self, dst_size: int) -> str: ... + def ReportEventState(self, event: SBEvent, file: Incomplete) -> None: ... + def AppendEventStateReport(self, event: SBEvent, result: SBCommandReturnObject) -> None: ... + def RemoteAttachToProcessWithID(self, pid: int, error: SBError) -> bool: ... + def RemoteLaunch( + self, + argv: Incomplete, + envp: Incomplete, + stdin_path: str, + stdout_path: str, + stderr_path: str, + working_directory: str, + launch_flags: int, + stop_at_entry: bool, + error: SBError, + ) -> bool: ... + def GetNumThreads(self) -> int: ... + def GetThreadAtIndex(self, index: int) -> SBThread: ... + def GetThreadByID(self, sb_thread_id: int) -> SBThread: ... + def GetThreadByIndexID(self, index_id: int) -> SBThread: ... + def GetSelectedThread(self) -> SBThread: ... + def CreateOSPluginThread(self, tid: int, context: int) -> SBThread: ... + def SetSelectedThread(self, thread: SBThread) -> bool: ... + def SetSelectedThreadByID(self, tid: int) -> bool: ... + def SetSelectedThreadByIndexID(self, index_id: int) -> bool: ... + def GetNumQueues(self) -> int: ... + def GetQueueAtIndex(self, index: int) -> SBQueue: ... + def GetState(self) -> int: ... + def GetExitStatus(self) -> int: ... + def GetExitDescription(self) -> str: ... + def GetProcessID(self) -> int: ... + def GetUniqueID(self) -> int: ... + def GetAddressByteSize(self) -> int: ... + def Destroy(self) -> SBError: ... + def Continue(self) -> SBError: ... + def Stop(self) -> SBError: ... + def Kill(self) -> SBError: ... + def Detach(self, keep_stopped: Union[bool, None] = None) -> SBError: ... + def Signal(self, signal: int) -> SBError: ... + def GetUnixSignals(self) -> SBUnixSignals: ... + def SendAsyncInterrupt(self) -> None: ... + def GetStopID(self, include_expression_stops: bool = False) -> int: ... + def GetStopEventForStopID(self, stop_id: int) -> SBEvent: ... + def ForceScriptedState(self, new_state: int) -> None: ... + def ReadMemory(self, addr: int, len: int, error: SBError) -> Union[bytes, None]: ... + def WriteMemory(self, addr: int, buf: bytes, error: SBError) -> int: ... + def ReadCStringFromMemory(self, addr: int, len: int, error: SBError) -> str: ... + def ReadUnsignedFromMemory(self, addr: int, byte_size: int, error: SBError) -> int: ... + def ReadPointerFromMemory(self, addr: int, error: SBError) -> int: ... + def FindRangesInMemory( + self, + buf: str, + ranges: SBAddressRangeList, + alignment: int, + max_matches: int, + error: SBError, + ) -> SBAddressRangeList: ... + def FindInMemory(self, buf: str, range: SBAddressRange, alignment: int, error: SBError) -> int: ... + @staticmethod + def GetStateFromEvent(event: SBEvent) -> int: ... + @staticmethod + def GetRestartedFromEvent(event: SBEvent) -> bool: ... + @staticmethod + def GetNumRestartedReasonsFromEvent(event: SBEvent) -> int: ... + @staticmethod + def GetRestartedReasonAtIndexFromEvent(event: SBEvent, idx: int) -> str: ... + @staticmethod + def GetProcessFromEvent(event: SBEvent) -> SBProcess: ... + @staticmethod + def GetInterruptedFromEvent(event: SBEvent) -> bool: ... + @staticmethod + def GetStructuredDataFromEvent(event: SBEvent) -> SBStructuredData: ... + @staticmethod + def EventIsProcessEvent(event: SBEvent) -> bool: ... + @staticmethod + def EventIsStructuredDataEvent(event: SBEvent) -> bool: ... + def GetBroadcaster(self) -> SBBroadcaster: ... + @staticmethod + def GetBroadcasterClass() -> str: ... + def GetDescription(self, description: SBStream) -> bool: ... + def GetExtendedCrashInformation(self) -> SBStructuredData: ... + def GetNumSupportedHardwareWatchpoints(self, error: SBError) -> int: ... + def LoadImage(self, *args: Incomplete) -> int: ... + def LoadImageUsingPaths( + self, + image_spec: SBFileSpec, + paths: SBStringList, + loaded_path: SBFileSpec, + error: SBError, + ) -> int: ... + def UnloadImage(self, image_token: int) -> SBError: ... + def SendEventData(self, data: str) -> SBError: ... + def GetNumExtendedBacktraceTypes(self) -> int: ... + def GetExtendedBacktraceTypeAtIndex(self, idx: int) -> str: ... + def GetHistoryThreads(self, addr: int) -> SBThreadCollection: ... + def IsInstrumentationRuntimePresent(self, type: Incomplete) -> bool: ... + def SaveCore(self, *args: Incomplete) -> SBError: ... + def GetMemoryRegionInfo(self, load_addr: int, region_info: SBMemoryRegionInfo) -> SBError: ... + def GetMemoryRegions(self) -> SBMemoryRegionInfoList: ... + def GetProcessInfo(self) -> SBProcessInfo: ... + def GetCoreFile(self) -> SBFileSpec: ... + def GetAddressMask(self, type: int, addr_range: Union[int, None] = None) -> int: ... + def SetAddressMask(self, type: int, mask: int, addr_range: Union[int, None] = None) -> None: ... + def SetAddressableBits(self, type: int, num_bits: int, addr_range: int) -> None: ... + def FixAddress(self, addr: int, type: int) -> int: ... + def AllocateMemory(self, size: int, permissions: int, error: SBError) -> int: ... + def DeallocateMemory(self, ptr: int) -> SBError: ... + def GetScriptedImplementation(self) -> Union[SBScriptObject, None]: ... + def GetStatus(self, status: SBStream) -> None: ... + def WriteMemoryAsCString(self, addr: int, buf: str, error: SBError) -> int: ... + def __get_is_alive__(self) -> bool: ... + def __get_is_running__(self) -> bool: ... + def __get_is_stopped__(self) -> bool: ... + + class threads_access: + sbprocess: SBProcess + def __init__(self, sbprocess: SBProcess) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + + def get_threads_access_object(self) -> threads_access: ... + def get_process_thread_list(self) -> list[Incomplete]: ... + def __iter__(self) -> Iterator[SBThread]: ... + def __len__(self) -> int: ... + def __int__(self) -> int: ... + threads: list[SBThread] + thread: Incomplete + is_alive: bool + is_running: bool + is_stopped: bool + id: int + target: SBTarget + num_threads: int + selected_thread: SBThread + state: int + exit_state: int + exit_description: str + broadcaster: SBBroadcaster + +class SBProcessInfo: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetName(self) -> str: ... + def GetExecutableFile(self) -> SBFileSpec: ... + def GetProcessID(self) -> int: ... + def GetUserID(self) -> int: ... + def GetGroupID(self) -> int: ... + def UserIDIsValid(self) -> bool: ... + def GroupIDIsValid(self) -> bool: ... + def GetEffectiveUserID(self) -> int: ... + def GetEffectiveGroupID(self) -> int: ... + def EffectiveUserIDIsValid(self) -> bool: ... + def EffectiveGroupIDIsValid(self) -> bool: ... + def GetParentProcessID(self) -> int: ... + def GetTriple(self) -> str: ... + +class SBProcessInfoList: + thisown: Incomplete + __swig_destroy__: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + def GetSize(self) -> int: ... + def GetProcessInfoAtIndex(self, idx: int, info: SBProcessInfo) -> bool: ... + def Clear(self) -> None: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[SBProcessInfo]: ... + +class SBProgress: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def Increment(self, amount: int, description: Union[str, None] = None) -> None: ... + +class SBQueue: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def Clear(self) -> None: ... + def GetProcess(self) -> SBProcess: ... + def GetQueueID(self) -> int: ... + def GetName(self) -> str: ... + def GetIndexID(self) -> int: ... + def GetNumThreads(self) -> int: ... + def GetThreadAtIndex(self, arg2: int) -> SBThread: ... + def GetNumPendingItems(self) -> int: ... + def GetPendingItemAtIndex(self, arg2: int) -> SBQueueItem: ... + def GetNumRunningItems(self) -> int: ... + def GetKind(self) -> int: ... + +class SBQueueItem: + thisown: Incomplete + def __init__(self) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def Clear(self) -> None: ... + def GetKind(self) -> int: ... + def SetKind(self, kind: int) -> None: ... + def GetAddress(self) -> SBAddress: ... + def SetAddress(self, addr: SBAddress) -> None: ... + def GetExtendedBacktraceThread(self, type: str) -> SBThread: ... + def __hex__(self) -> Incomplete: ... + +class SBReproducer: + thisown: Incomplete + @staticmethod + def Capture(path: str) -> str: ... + @staticmethod + def PassiveReplay(path: str) -> str: ... + @staticmethod + def SetAutoGenerate(b: bool) -> bool: ... + @staticmethod + def SetWorkingDirectory(path: str) -> None: ... + def __init__(self) -> None: ... + __swig_destroy__: Incomplete + +class SBScriptObject: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def __ne__(self, rhs: Any) -> bool: ... + def IsValid(self) -> bool: ... + def GetPointer(self) -> Incomplete: ... + def GetLanguage(self) -> Incomplete: ... + def __eq__(self, other: Any) -> bool: ... + ptr: Incomplete + lang: Incomplete + +class SBSection: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetName(self) -> str: ... + def GetParent(self) -> SBSection: ... + def FindSubSection(self, sect_name: str) -> SBSection: ... + def GetNumSubSections(self) -> int: ... + def GetSubSectionAtIndex(self, idx: int) -> SBSection: ... + def GetFileAddress(self) -> int: ... + def GetLoadAddress(self, target: SBTarget) -> int: ... + def GetByteSize(self) -> int: ... + def GetFileOffset(self) -> int: ... + def GetFileByteSize(self) -> int: ... + def GetSectionData(self, offset: int = 0, size: int = UINT64_MAX) -> SBData: ... + def GetSectionType(self) -> int: ... + def GetPermissions(self) -> int: ... + def GetTargetByteSize(self) -> int: ... + def GetAlignment(self) -> int: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def GetDescription(self, description: SBStream) -> bool: ... + def __iter__(self) -> Iterator[SBSection]: ... + def __len__(self) -> int: ... + def get_addr(self) -> Incomplete: ... + name: str + addr: SBAddress + file_addr: int + size: int + file_offset: int + file_size: int + data: SBData + type: int + target_byte_size: int + alignment: int + +class SBSourceManager: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def DisplaySourceLinesWithLineNumbers( + self, + file: SBFileSpec, + line: int, + context_before: int, + context_after: int, + current_line_cstr: str, + s: SBStream, + ) -> int: ... + def DisplaySourceLinesWithLineNumbersAndColumn( + self, + file: SBFileSpec, + line: int, + column: int, + context_before: int, + context_after: int, + current_line_cstr: str, + s: SBStream, + ) -> int: ... + +class SBStatisticsOptions: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def SetSummaryOnly(self, b: bool) -> None: ... + def GetSummaryOnly(self) -> bool: ... + def SetIncludeTargets(self, b: bool) -> None: ... + def GetIncludeTargets(self) -> bool: ... + def SetIncludeModules(self, b: bool) -> None: ... + def GetIncludeModules(self) -> bool: ... + def SetIncludeTranscript(self, b: bool) -> None: ... + def GetIncludeTranscript(self) -> bool: ... + def SetReportAllAvailableDebugInfo(self, b: bool) -> None: ... + def GetReportAllAvailableDebugInfo(self) -> bool: ... + +class SBStream: + thisown: Incomplete + def __init__(self) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetData(self) -> str: ... + def GetSize(self) -> int: ... + def Print(self, str: str) -> None: ... + def RedirectToFile(self, *args: Incomplete) -> None: ... + def RedirectToFileDescriptor(self, fd: int, transfer_fh_ownership: bool) -> None: ... + def Clear(self) -> None: ... + def __len__(self) -> int: ... + def RedirectToFileHandle(self, file: Incomplete, transfer_fh_ownership: bool) -> None: ... + def write(self, str: str) -> None: ... + def flush(self) -> None: ... + +class SBStringList: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def AppendString(self, str: str) -> None: ... + def AppendList(self, *args: Incomplete) -> None: ... + def GetSize(self) -> int: ... + def GetStringAtIndex(self, idx: int) -> str: ... + def Clear(self) -> None: ... + def __iter__(self) -> Iterator[str]: ... + def __len__(self) -> int: ... + +class SBStructuredData: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def SetFromJSON(self, json: Union[str, SBStream]) -> SBError: ... + def Clear(self) -> None: ... + def GetAsJSON(self, stream: SBStream) -> SBError: ... + def GetDescription(self, stream: SBStream) -> SBError: ... + def GetType(self) -> int: ... + def GetSize(self) -> int: ... + def GetKeys(self, keys: SBStringList) -> bool: ... + def GetValueForKey(self, key: str) -> SBStructuredData: ... + def GetItemAtIndex(self, idx: int) -> SBStructuredData: ... + def GetUnsignedIntegerValue(self, fail_value: int = 0) -> int: ... + def GetSignedIntegerValue(self, fail_value: int = 0) -> int: ... + def GetIntegerValue(self, fail_value: int = 0) -> int: ... + def GetFloatValue(self, fail_value: float = 0.0) -> float: ... + def GetBooleanValue(self, fail_value: bool = False) -> bool: ... + def GetStringValue(self, max_len: int) -> str: ... + def GetGenericValue(self) -> SBScriptObject: ... + def __int__(self) -> int: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[SBStructuredData]: ... + +class SBSymbol: + thisown: Incomplete + __swig_destroy__: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetName(self) -> str: ... + def GetDisplayName(self) -> str: ... + def GetMangledName(self) -> str: ... + def GetInstructions(self, target: SBTarget, flavor_string: Union[str, None] = None) -> SBInstructionList: ... + def GetStartAddress(self) -> SBAddress: ... + def GetEndAddress(self) -> SBAddress: ... + def GetValue(self) -> int: ... + def GetSize(self) -> int: ... + def GetPrologueByteSize(self) -> int: ... + def GetType(self) -> int: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def GetDescription(self, description: SBStream) -> bool: ... + def IsExternal(self) -> bool: ... + def IsSynthetic(self) -> bool: ... + def __hex__(self) -> Incomplete: ... + def get_instructions_from_current_target(self) -> Incomplete: ... + name: str + mangled: str + type: int + addr: SBAddress + end_addr: SBAddress + prologue_size: int + instructions: SBInstructionList + external: bool + synthetic: bool + +class SBSymbolContext: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetModule(self) -> SBModule: ... + def GetCompileUnit(self) -> SBCompileUnit: ... + def GetFunction(self) -> SBFunction: ... + def GetBlock(self) -> SBBlock: ... + def GetLineEntry(self) -> SBLineEntry: ... + def GetSymbol(self) -> SBSymbol: ... + def SetModule(self, module: SBModule) -> None: ... + def SetCompileUnit(self, compile_unit: SBCompileUnit) -> None: ... + def SetFunction(self, function: SBFunction) -> None: ... + def SetBlock(self, block: SBBlock) -> None: ... + def SetLineEntry(self, line_entry: SBLineEntry) -> None: ... + def SetSymbol(self, symbol: SBSymbol) -> None: ... + def GetParentOfInlinedScope(self, curr_frame_pc: SBAddress, parent_frame_addr: SBAddress) -> SBSymbolContext: ... + def GetDescription(self, description: SBStream) -> bool: ... + module: SBModule + compile_unit: SBCompileUnit + function: SBFunction + block: SBBlock + symbol: SBSymbol + line_entry: SBLineEntry + +class SBSymbolContextList: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetSize(self) -> int: ... + def GetContextAtIndex(self, idx: int) -> SBSymbolContext: ... + def GetDescription(self, description: SBStream) -> bool: ... + def Append(self, sc: Union[SBSymbolContext, SBSymbolContextList]) -> None: ... + def Clear(self) -> None: ... + def __iter__(self) -> Iterator[SBSymbolContext]: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + def get_module_array(self) -> Incomplete: ... + def get_compile_unit_array(self) -> Incomplete: ... + def get_function_array(self) -> Incomplete: ... + def get_block_array(self) -> Incomplete: ... + def get_symbol_array(self) -> Incomplete: ... + def get_line_entry_array(self) -> Incomplete: ... + modules: list[SBModule] + compile_units: list[SBCompileUnit] + functions: list[SBFunction] + blocks: list[SBBlock] + line_entries: list[SBLineEntry] + symbols: list[SBSymbol] + +class SBTarget: + thisown: Incomplete + eBroadcastBitBreakpointChanged: int + eBroadcastBitModulesLoaded: int + eBroadcastBitModulesUnloaded: int + eBroadcastBitWatchpointChanged: int + eBroadcastBitSymbolsLoaded: int + eBroadcastBitSymbolsChanged: int + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + @staticmethod + def EventIsTargetEvent(event: SBEvent) -> bool: ... + @staticmethod + def GetTargetFromEvent(event: SBEvent) -> SBTarget: ... + @staticmethod + def GetNumModulesFromEvent(event: SBEvent) -> int: ... + @staticmethod + def GetModuleAtIndexFromEvent(idx: int, event: SBEvent) -> SBModule: ... + @staticmethod + def GetBroadcasterClassName() -> str: ... + def GetProcess(self) -> SBProcess: ... + def SetCollectingStats(self, v: bool) -> None: ... + def GetCollectingStats(self) -> bool: ... + def GetStatistics(self, options: Union[SBStatisticsOptions, None] = None) -> SBStructuredData: ... + def ResetStatistics(self) -> None: ... + def GetPlatform(self) -> SBPlatform: ... + def GetEnvironment(self) -> SBEnvironment: ... + def Install(self) -> SBError: ... + def LoadCore(self, core_file: str, error: Union[SBError, None] = None) -> SBProcess: ... + def LaunchSimple(self, *args: Incomplete) -> SBProcess: ... + def Launch(self, *args: Incomplete) -> SBProcess: ... + def Attach(self, attach_info: SBAttachInfo, error: SBError) -> SBProcess: ... + def AttachToProcessWithID(self, listener: SBListener, pid: int, error: SBError) -> SBProcess: ... + def AttachToProcessWithName(self, listener: SBListener, name: str, wait_for: bool, error: SBError) -> SBProcess: ... + def ConnectRemote(self, listener: SBListener, url: str, plugin_name: str, error: SBError) -> SBProcess: ... + def GetExecutable(self) -> SBFileSpec: ... + def AppendImageSearchPath(self, _from: str, to: str, error: SBError) -> None: ... + def AddModule(self, *args: Incomplete) -> Incomplete: ... + def GetNumModules(self) -> int: ... + def GetModuleAtIndex(self, idx: int) -> SBModule: ... + def RemoveModule(self, module: SBModule) -> bool: ... + def GetDebugger(self) -> SBDebugger: ... + def FindModule(self, file_spec: SBFileSpec) -> SBModule: ... + def FindCompileUnits(self, sb_file_spec: SBFileSpec) -> SBSymbolContextList: ... + def GetByteOrder(self) -> int: ... + def GetAddressByteSize(self) -> int: ... + def GetTriple(self) -> str: ... + def GetABIName(self) -> str: ... + def GetLabel(self) -> str: ... + def SetLabel(self, label: str) -> SBError: ... + def GetDataByteSize(self) -> int: ... + def GetCodeByteSize(self) -> int: ... + def GetMaximumNumberOfChildrenToDisplay(self) -> int: ... + def SetSectionLoadAddress(self, section: SBSection, section_base_addr: int) -> SBError: ... + def ClearSectionLoadAddress(self, section: SBSection) -> SBError: ... + def SetModuleLoadAddress(self, module: SBModule, sections_offset: int) -> SBError: ... + def ClearModuleLoadAddress(self, module: SBModule) -> SBError: ... + def FindFunctions(self, name: str, name_type_mask: int) -> SBSymbolContextList: ... + def FindFirstGlobalVariable(self, name: str) -> SBValue: ... + def FindGlobalVariables(self, name: str, max_matches: int, matchtype: Union[int, None] = None) -> SBValueList: ... + def FindGlobalFunctions(self, name: str, max_matches: int, matchtype: int) -> SBSymbolContextList: ... + def Clear(self) -> None: ... + def ResolveFileAddress(self, file_addr: int) -> SBAddress: ... + def ResolveLoadAddress(self, vm_addr: int) -> SBAddress: ... + def ResolvePastLoadAddress(self, stop_id: int, vm_addr: int) -> SBAddress: ... + def ResolveSymbolContextForAddress(self, addr: SBAddress, resolve_scope: int) -> SBSymbolContext: ... + def ReadMemory(self, addr: SBAddress, num: int, error: SBError) -> Union[bytes, None]: ... + def BreakpointCreateByLocation(self, *args: Incomplete) -> SBBreakpoint: ... + def BreakpointCreateByName(self, *args: Incomplete) -> SBBreakpoint: ... + def BreakpointCreateByNames(self, *args: Incomplete) -> SBBreakpoint: ... + def BreakpointCreateByRegex(self, *args: Incomplete) -> SBBreakpoint: ... + def BreakpointCreateBySourceRegex(self, *args: Incomplete) -> SBBreakpoint: ... + def BreakpointCreateForException(self, language: int, catch_bp: bool, throw_bp: bool) -> SBBreakpoint: ... + def BreakpointCreateByAddress(self, address: int) -> SBBreakpoint: ... + def BreakpointCreateBySBAddress(self, address: SBAddress) -> SBBreakpoint: ... + def BreakpointCreateFromScript( + self, + class_name: str, + extra_args: SBStructuredData, + module_list: SBFileSpecList, + file_list: SBFileSpecList, + request_hardware: bool = False, + ) -> SBBreakpoint: ... + def BreakpointsCreateFromFile(self, *args: Incomplete) -> SBError: ... + def BreakpointsWriteToFile(self, *args: Incomplete) -> SBError: ... + def GetNumBreakpoints(self) -> int: ... + def GetBreakpointAtIndex(self, idx: int) -> SBBreakpoint: ... + def BreakpointDelete(self, break_id: int) -> bool: ... + def FindBreakpointByID(self, break_id: int) -> SBBreakpoint: ... + def FindBreakpointsByName(self, name: str, bkpt_list: SBBreakpointList) -> bool: ... + def GetBreakpointNames(self, names: SBStringList) -> None: ... + def DeleteBreakpointName(self, name: str) -> None: ... + def EnableAllBreakpoints(self) -> bool: ... + def DisableAllBreakpoints(self) -> bool: ... + def DeleteAllBreakpoints(self) -> bool: ... + def GetNumWatchpoints(self) -> int: ... + def GetWatchpointAtIndex(self, idx: int) -> SBWatchpoint: ... + def DeleteWatchpoint(self, watch_id: int) -> bool: ... + def FindWatchpointByID(self, watch_id: int) -> SBWatchpoint: ... + def WatchAddress(self, addr: int, size: int, read: bool, modify: bool, error: SBError) -> SBWatchpoint: ... + def WatchpointCreateByAddress( + self, addr: int, size: int, options: SBWatchpointOptions, error: SBError + ) -> SBWatchpoint: ... + def EnableAllWatchpoints(self) -> bool: ... + def DisableAllWatchpoints(self) -> bool: ... + def DeleteAllWatchpoints(self) -> bool: ... + def GetBroadcaster(self) -> SBBroadcaster: ... + def FindFirstType(self, type: str) -> SBType: ... + def FindTypes(self, type: str) -> SBTypeList: ... + def GetBasicType(self, type: int) -> SBType: ... + def CreateValueFromAddress(self, name: str, addr: SBAddress, type: SBType) -> SBValue: ... + def CreateValueFromData(self, name: str, data: SBData, type: SBType) -> SBValue: ... + def CreateValueFromExpression(self, name: str, expr: str) -> SBValue: ... + def GetSourceManager(self) -> SBSourceManager: ... + def ReadInstructions(self, start_addr: SBAddress, *args: Incomplete) -> SBInstructionList: ... + def GetInstructions(self, base_addr: SBAddress, buf: Incomplete) -> SBInstructionList: ... + def GetInstructionsWithFlavor( + self, base_addr: SBAddress, flavor_string: str, buf: Incomplete + ) -> SBInstructionList: ... + def FindSymbols(self, name: str, type: int) -> SBSymbolContextList: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def GetDescription(self, description: SBStream, description_level: int) -> bool: ... + def EvaluateExpression(self, expr: str, options: Union[SBExpressionOptions, None] = None) -> SBValue: ... + def GetStackRedZoneSize(self) -> int: ... + def IsLoaded(self, module: SBModule) -> bool: ... + def GetLaunchInfo(self) -> SBLaunchInfo: ... + def SetLaunchInfo(self, launch_info: SBLaunchInfo) -> None: ... + def GetTrace(self) -> SBTrace: ... + def CreateTrace(self, error: SBError) -> SBTrace: ... + + class modules_access: + sbtarget: SBTarget + def __init__(self, sbtarget: SBTarget) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + + def get_modules_access_object(self) -> modules_access: ... + def get_modules_array(self) -> list[Incomplete]: ... + def module_iter(self) -> Incomplete: ... + def breakpoint_iter(self) -> Incomplete: ... + + class bkpts_access: + sbtarget: SBTarget + def __init__(self, sbtarget: SBTarget) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + + def get_bkpts_access_object(self) -> bkpts_access: ... + def get_target_bkpts(self) -> list[Incomplete]: ... + def watchpoint_iter(self) -> Incomplete: ... + + class watchpoints_access: + sbtarget: SBTarget + def __init__(self, sbtarget: SBTarget) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + + def get_watchpoints_access_object(self) -> watchpoints_access: ... + def get_target_watchpoints(self) -> list[Incomplete]: ... + modules: list[SBModule] + module: Incomplete + process: SBProcess + executable: SBModule + debugger: SBDebugger + num_breakpoints: int + breakpoints: list[SBBreakpoint] + breakpoint: Incomplete + num_watchpoints: int + watchpoints: list[SBWatchpoint] + watchpoint: Incomplete + broadcaster: SBBroadcaster + byte_order: int + addr_size: int + triple: str + data_byte_size: int + code_byte_size: int + platform: SBPlatform + +class SBThread: + thisown: Incomplete + eBroadcastBitStackChanged: int + eBroadcastBitThreadSuspended: int + eBroadcastBitThreadResumed: int + eBroadcastBitSelectedFrameChanged: int + eBroadcastBitThreadSelected: int + @staticmethod + def GetBroadcasterClassName() -> str: ... + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def GetQueue(self) -> SBQueue: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def Clear(self) -> None: ... + def GetStopReason(self) -> int: ... + def GetStopReasonDataCount(self) -> int: ... + def GetStopReasonDataAtIndex(self, idx: int) -> int: ... + def GetStopReasonExtendedInfoAsJSON(self, stream: SBStream) -> bool: ... + def GetStopReasonExtendedBacktraces(self, type: int) -> SBThreadCollection: ... + def GetStopDescription(self, len: int) -> str: ... + def GetStopReturnValue(self) -> SBValue: ... + def GetThreadID(self) -> int: ... + def GetIndexID(self) -> int: ... + def GetName(self) -> str: ... + def GetQueueName(self) -> str: ... + def GetQueueID(self) -> int: ... + def GetInfoItemByPathAsString(self, path: str, strm: SBStream) -> bool: ... + def StepOver(self, stop_other_threads: int, error: Union[SBError, None] = None) -> None: ... + def StepInto(self, *args: Incomplete) -> None: ... + def StepOut(self, error: Union[SBError, None] = None) -> None: ... + def StepOutOfFrame(self, frame: SBFrame, error: Union[SBError, None] = None) -> None: ... + def StepInstruction(self, step_over: bool, error: Union[SBError, None] = None) -> None: ... + def StepOverUntil(self, frame: SBFrame, file_spec: SBFileSpec, line: int) -> SBError: ... + def StepUsingScriptedThreadPlan(self, script_class_name: str, *args: Incomplete) -> SBError: ... + def JumpToLine(self, file_spec: SBFileSpec, line: int) -> SBError: ... + def RunToAddress(self, addr: int, error: Union[SBError, None] = None) -> None: ... + def ReturnFromFrame(self, frame: SBFrame, return_value: SBValue) -> SBError: ... + def UnwindInnermostExpression(self) -> SBError: ... + def Suspend(self, error: Union[SBError, None] = None) -> bool: ... + def Resume(self, error: Union[SBError, None] = None) -> bool: ... + def IsSuspended(self) -> bool: ... + def IsStopped(self) -> bool: ... + def GetNumFrames(self) -> int: ... + def GetFrameAtIndex(self, idx: int) -> SBFrame: ... + def GetSelectedFrame(self) -> SBFrame: ... + def SetSelectedFrame(self, frame_idx: int) -> SBFrame: ... + @staticmethod + def EventIsThreadEvent(event: SBEvent) -> bool: ... + @staticmethod + def GetStackFrameFromEvent(event: SBEvent) -> SBFrame: ... + @staticmethod + def GetThreadFromEvent(event: SBEvent) -> SBThread: ... + def GetProcess(self) -> SBProcess: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def GetDescription(self, desc: SBStream, stop_format: Union[bool, None] = None) -> bool: ... + def GetDescriptionWithFormat(self, format: SBFormat, output: SBStream) -> SBError: ... + def GetStatus(self, status: SBStream) -> bool: ... + def GetExtendedBacktraceThread(self, type: str) -> SBThread: ... + def GetExtendedBacktraceOriginatingIndexID(self) -> int: ... + def GetCurrentException(self) -> SBValue: ... + def GetCurrentExceptionBacktrace(self) -> SBThread: ... + def SafeToCallFunctions(self) -> bool: ... + def GetSiginfo(self) -> SBValue: ... + def __iter__(self) -> Iterator[SBFrame]: ... + def __len__(self) -> int: ... + + class frames_access: + sbthread: SBThread + def __init__(self, sbthread: SBThread) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + + def get_frames_access_object(self) -> frames_access: ... + def get_thread_frames(self) -> list[Incomplete]: ... + def get_stop_reason_data(self) -> Incomplete: ... + def set_selected_frame(self, frame: Incomplete) -> None: ... + id: int + idx: int + return_value: SBValue + process: SBProcess + num_frames: int + frames: list[SBFrame] + frame: Incomplete + name: str + queue: str + queue_id: int + stop_reason: int + stop_reason_data: list[Incomplete] + is_suspended: bool + is_stopped: bool + selected_frame: SBFrame + +class SBThreadCollection: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetSize(self) -> int: ... + def GetThreadAtIndex(self, idx: int) -> SBThread: ... + def __iter__(self) -> Iterator[SBThread]: ... + def __len__(self) -> int: ... + +class SBThreadPlan: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def Clear(self) -> None: ... + def GetStopReason(self) -> int: ... + def GetStopReasonDataCount(self) -> int: ... + def GetStopReasonDataAtIndex(self, idx: int) -> int: ... + def GetThread(self) -> SBThread: ... + def GetDescription(self, description: SBStream) -> bool: ... + def SetPlanComplete(self, success: bool) -> None: ... + def IsPlanComplete(self) -> bool: ... + def IsPlanStale(self) -> bool: ... + def IsValid(self) -> bool: ... + def GetStopOthers(self) -> bool: ... + def SetStopOthers(self, stop_others: bool) -> None: ... + def QueueThreadPlanForStepOverRange( + self, start_address: SBAddress, range_size: int, error: Union[SBError, None] = None + ) -> SBThreadPlan: ... + def QueueThreadPlanForStepInRange( + self, start_address: SBAddress, range_size: int, error: Union[SBError, None] = None + ) -> SBThreadPlan: ... + def QueueThreadPlanForStepOut( + self, frame_idx_to_step_to: int, first_insn: bool, error: Union[SBError, None] = None + ) -> SBThreadPlan: ... + def QueueThreadPlanForRunToAddress( + self, address: SBAddress, error: Union[SBError, None] = None + ) -> SBThreadPlan: ... + def QueueThreadPlanForStepScripted(self, script_class_name: str, *args: Incomplete) -> SBThreadPlan: ... + +class SBTrace: + thisown: Incomplete + def __init__(self) -> None: ... + @staticmethod + def LoadTraceFromFile(error: SBError, debugger: SBDebugger, trace_description_file: SBFileSpec) -> SBTrace: ... + def CreateNewCursor(self, error: SBError, thread: SBThread) -> SBTraceCursor: ... + def SaveToDisk(self, error: SBError, bundle_dir: SBFileSpec, compact: bool = False) -> SBFileSpec: ... + def GetStartConfigurationHelp(self) -> str: ... + def Start(self, *args: Incomplete) -> SBError: ... + def Stop(self, thread: Union[SBThread, None] = None) -> SBError: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + __swig_destroy__: Incomplete + +class SBTraceCursor: + thisown: Incomplete + def __init__(self) -> None: ... + def SetForwards(self, forwards: bool) -> None: ... + def IsForwards(self) -> bool: ... + def Next(self) -> None: ... + def HasValue(self) -> bool: ... + def GoToId(self, id: int) -> bool: ... + def HasId(self, id: int) -> bool: ... + def GetId(self) -> int: ... + def Seek(self, offset: int, origin: int) -> bool: ... + def GetItemKind(self) -> int: ... + def IsError(self) -> bool: ... + def GetError(self) -> str: ... + def IsEvent(self) -> bool: ... + def GetEventType(self) -> int: ... + def GetEventTypeAsString(self) -> str: ... + def IsInstruction(self) -> bool: ... + def GetLoadAddress(self) -> int: ... + def GetCPU(self) -> int: ... + def IsValid(self) -> bool: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + __swig_destroy__: Incomplete + +class SBTypeMember: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetName(self) -> str: ... + def GetType(self) -> SBType: ... + def GetOffsetInBytes(self) -> int: ... + def GetOffsetInBits(self) -> int: ... + def IsBitfield(self) -> bool: ... + def GetBitfieldSizeInBits(self) -> int: ... + def GetDescription(self, description: SBStream, description_level: int) -> bool: ... + def __eq__(self, other: Any) -> bool: ... + name: str + type: SBType + byte_offset: int + bit_offset: int + is_bitfield: bool + bitfield_bit_size: int + +class SBTypeMemberFunction: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetName(self) -> str: ... + def GetDemangledName(self) -> str: ... + def GetMangledName(self) -> str: ... + def GetType(self) -> SBType: ... + def GetReturnType(self) -> SBType: ... + def GetNumberOfArguments(self) -> int: ... + def GetArgumentTypeAtIndex(self, arg2: int) -> SBType: ... + def GetKind(self) -> int: ... + def GetDescription(self, description: SBStream, description_level: int) -> bool: ... + def __eq__(self, other: Any) -> bool: ... + +class SBTypeStaticField: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetName(self) -> str: ... + def GetMangledName(self) -> str: ... + def GetType(self) -> SBType: ... + def GetConstantValue(self, target: SBTarget) -> SBValue: ... + +class SBType: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetByteSize(self) -> int: ... + def GetByteAlign(self) -> int: ... + def IsPointerType(self) -> bool: ... + def IsReferenceType(self) -> bool: ... + def IsFunctionType(self) -> bool: ... + def IsPolymorphicClass(self) -> bool: ... + def IsArrayType(self) -> bool: ... + def IsVectorType(self) -> bool: ... + def IsTypedefType(self) -> bool: ... + def IsAnonymousType(self) -> bool: ... + def IsScopedEnumerationType(self) -> bool: ... + def IsAggregateType(self) -> bool: ... + def GetPointerType(self) -> SBType: ... + def GetPointeeType(self) -> SBType: ... + def GetReferenceType(self) -> SBType: ... + def GetTypedefedType(self) -> SBType: ... + def GetDereferencedType(self) -> SBType: ... + def GetUnqualifiedType(self) -> SBType: ... + def GetArrayElementType(self) -> SBType: ... + def GetArrayType(self, size: int) -> SBType: ... + def GetVectorElementType(self) -> SBType: ... + def GetCanonicalType(self) -> SBType: ... + def GetEnumerationIntegerType(self) -> SBType: ... + def GetBasicType(self, type: Union[int, None] = None) -> Union[int, SBType]: ... + def GetNumberOfFields(self) -> int: ... + def GetNumberOfDirectBaseClasses(self) -> int: ... + def GetNumberOfVirtualBaseClasses(self) -> int: ... + def GetFieldAtIndex(self, idx: int) -> SBTypeMember: ... + def GetDirectBaseClassAtIndex(self, idx: int) -> SBTypeMember: ... + def GetVirtualBaseClassAtIndex(self, idx: int) -> SBTypeMember: ... + def GetStaticFieldWithName(self, name: str) -> SBTypeStaticField: ... + def GetEnumMembers(self) -> SBTypeEnumMemberList: ... + def GetNumberOfTemplateArguments(self) -> int: ... + def GetTemplateArgumentType(self, idx: int) -> SBType: ... + def GetTemplateArgumentKind(self, idx: int) -> int: ... + def GetFunctionReturnType(self) -> SBType: ... + def GetFunctionArgumentTypes(self) -> SBTypeList: ... + def GetNumberOfMemberFunctions(self) -> int: ... + def GetMemberFunctionAtIndex(self, idx: int) -> SBTypeMemberFunction: ... + def GetModule(self) -> SBModule: ... + def GetName(self) -> str: ... + def GetDisplayTypeName(self) -> str: ... + def GetTypeClass(self) -> int: ... + def IsTypeComplete(self) -> bool: ... + def GetTypeFlags(self) -> int: ... + def GetDescription(self, description: SBStream, description_level: int) -> bool: ... + def FindDirectNestedType(self, name: str) -> SBType: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def template_arg_array(self) -> Incomplete: ... + def __len__(self) -> int: ... + module: SBModule + name: str + size: int + is_pointer: bool + is_reference: bool + num_fields: int + num_bases: int + num_vbases: int + num_template_args: int + template_args: list[SBType] + type: int + is_complete: bool + def get_bases_array(self) -> list[Incomplete]: ... + def get_vbases_array(self) -> list[Incomplete]: ... + def get_fields_array(self) -> list[Incomplete]: ... + def get_members_array(self) -> list[Incomplete]: ... + def get_enum_members_array(self) -> list[Incomplete]: ... + bases: list[SBTypeMember] + vbases: list[SBTypeMember] + fields: list[SBTypeMember] + members: list[SBTypeMember] + enum_members: list[SBTypeEnumMember] + +class SBTypeList: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def Append(self, type: SBType) -> None: ... + def GetTypeAtIndex(self, index: int) -> SBType: ... + def GetSize(self) -> int: ... + def __eq__(self, other: Any) -> bool: ... + def __iter__(self) -> Iterator[SBType]: ... + def __len__(self) -> int: ... + +class SBTypeCategory: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetEnabled(self) -> bool: ... + def SetEnabled(self, arg2: bool) -> None: ... + def GetName(self) -> str: ... + def GetLanguageAtIndex(self, idx: int) -> int: ... + def GetNumLanguages(self) -> int: ... + def AddLanguage(self, language: int) -> None: ... + def GetDescription(self, description: SBStream, description_level: int) -> bool: ... + def GetNumFormats(self) -> int: ... + def GetNumSummaries(self) -> int: ... + def GetNumFilters(self) -> int: ... + def GetNumSynthetics(self) -> int: ... + def GetTypeNameSpecifierForFilterAtIndex(self, arg2: int) -> SBTypeNameSpecifier: ... + def GetTypeNameSpecifierForFormatAtIndex(self, arg2: int) -> SBTypeNameSpecifier: ... + def GetTypeNameSpecifierForSummaryAtIndex(self, arg2: int) -> SBTypeNameSpecifier: ... + def GetTypeNameSpecifierForSyntheticAtIndex(self, arg2: int) -> SBTypeNameSpecifier: ... + def GetFilterForType(self, arg2: SBTypeNameSpecifier) -> SBTypeFilter: ... + def GetFormatForType(self, arg2: SBTypeNameSpecifier) -> SBTypeFormat: ... + def GetSummaryForType(self, arg2: SBTypeNameSpecifier) -> SBTypeSummary: ... + def GetSyntheticForType(self, arg2: SBTypeNameSpecifier) -> SBTypeSynthetic: ... + def GetFilterAtIndex(self, arg2: int) -> SBTypeFilter: ... + def GetFormatAtIndex(self, arg2: int) -> SBTypeFormat: ... + def GetSummaryAtIndex(self, arg2: int) -> SBTypeSummary: ... + def GetSyntheticAtIndex(self, arg2: int) -> SBTypeSynthetic: ... + def AddTypeFormat(self, arg2: SBTypeNameSpecifier, arg3: SBTypeFormat) -> bool: ... + def DeleteTypeFormat(self, arg2: SBTypeNameSpecifier) -> bool: ... + def AddTypeSummary(self, arg2: SBTypeNameSpecifier, arg3: SBTypeSummary) -> bool: ... + def DeleteTypeSummary(self, arg2: SBTypeNameSpecifier) -> bool: ... + def AddTypeFilter(self, arg2: SBTypeNameSpecifier, arg3: SBTypeFilter) -> bool: ... + def DeleteTypeFilter(self, arg2: SBTypeNameSpecifier) -> bool: ... + def AddTypeSynthetic(self, arg2: SBTypeNameSpecifier, arg3: SBTypeSynthetic) -> bool: ... + def DeleteTypeSynthetic(self, arg2: SBTypeNameSpecifier) -> bool: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + + class formatters_access_class: + sbcategory: SBTypeCategory + get_count_function: Incomplete + get_at_index_function: Incomplete + get_by_name_function: Incomplete + regex_type: Incomplete + def __init__( + self, + sbcategory: SBTypeCategory, + get_count_function: Incomplete, + get_at_index_function: Incomplete, + get_by_name_function: Incomplete, + ) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + + def get_formats_access_object(self) -> formatters_access_class: ... + def get_formats_array(self) -> list[Incomplete]: ... + def get_summaries_access_object(self) -> Incomplete: ... + def get_summaries_array(self) -> list[Incomplete]: ... + def get_synthetics_access_object(self) -> Incomplete: ... + def get_synthetics_array(self) -> list[Incomplete]: ... + def get_filters_access_object(self) -> Incomplete: ... + def get_filters_array(self) -> list[Incomplete]: ... + formats: list[SBTypeFormat] + format: Incomplete + summaries: list[SBTypeSummary] + summary: Incomplete + filters: list[SBTypeFilter] + filter: Incomplete + synthetics: list[SBTypeSynthetic] + synthetic: Incomplete + num_formats: int + num_summaries: int + num_filters: int + num_synthetics: int + name: str + enabled: bool + +class SBTypeEnumMember: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetValueAsSigned(self) -> int: ... + def GetValueAsUnsigned(self) -> int: ... + def GetName(self) -> str: ... + def GetType(self) -> SBType: ... + def GetDescription(self, description: SBStream, description_level: int) -> bool: ... + def __iter__(self) -> Iterator[Incomplete]: ... + def __len__(self) -> int: ... + name: str + type: SBType + signed: int + unsigned: int + +class SBTypeEnumMemberList: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def Append(self, entry: SBTypeEnumMember) -> None: ... + def GetTypeEnumMemberAtIndex(self, index: int) -> SBTypeEnumMember: ... + def GetSize(self) -> int: ... + def __iter__(self) -> Iterator[SBTypeEnumMember]: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + +class SBTypeFilter: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetNumberOfExpressionPaths(self) -> int: ... + def GetExpressionPathAtIndex(self, i: int) -> str: ... + def ReplaceExpressionPathAtIndex(self, i: int, item: str) -> bool: ... + def AppendExpressionPath(self, item: str) -> None: ... + def Clear(self) -> None: ... + def GetOptions(self) -> int: ... + def SetOptions(self, arg2: int) -> None: ... + def GetDescription(self, description: SBStream, description_level: int) -> bool: ... + def IsEqualTo(self, rhs: SBTypeFilter) -> bool: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + options: int + count: int + +class SBTypeFormat: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetFormat(self) -> int: ... + def GetTypeName(self) -> str: ... + def GetOptions(self) -> int: ... + def SetFormat(self, arg2: int) -> None: ... + def SetTypeName(self, arg2: str) -> None: ... + def SetOptions(self, arg2: int) -> None: ... + def GetDescription(self, description: SBStream, description_level: int) -> bool: ... + def IsEqualTo(self, rhs: SBTypeFormat) -> bool: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + format: int + options: int + +class SBTypeNameSpecifier: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetName(self) -> str: ... + def GetType(self) -> SBType: ... + def GetMatchType(self) -> int: ... + def IsRegex(self) -> bool: ... + def GetDescription(self, description: SBStream, description_level: int) -> bool: ... + def IsEqualTo(self, rhs: SBTypeNameSpecifier) -> bool: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + name: str + is_regex: bool + +class SBTypeSummaryOptions: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetLanguage(self) -> int: ... + def GetCapping(self) -> bool: ... + def SetLanguage(self, arg2: int) -> None: ... + def SetCapping(self, arg2: bool) -> None: ... + +class SBTypeSummary: + thisown: Incomplete + @staticmethod + def CreateWithSummaryString(data: str, options: int = 0) -> SBTypeSummary: ... + @staticmethod + def CreateWithFunctionName(data: str, options: int = 0) -> SBTypeSummary: ... + @staticmethod + def CreateWithScriptCode(data: str, options: int = 0) -> SBTypeSummary: ... + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def IsFunctionCode(self) -> bool: ... + def IsFunctionName(self) -> bool: ... + def IsSummaryString(self) -> bool: ... + def GetData(self) -> str: ... + def SetSummaryString(self, data: str) -> None: ... + def SetFunctionName(self, data: str) -> None: ... + def SetFunctionCode(self, data: str) -> None: ... + def GetOptions(self) -> int: ... + def SetOptions(self, arg2: int) -> None: ... + def GetDescription(self, description: SBStream, description_level: int) -> bool: ... + def DoesPrintValue(self, value: SBValue) -> bool: ... + def IsEqualTo(self, rhs: SBTypeSummary) -> bool: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + options: int + is_summary_string: bool + is_function_name: bool + summary_data: str + +class SBTypeSynthetic: + thisown: Incomplete + @staticmethod + def CreateWithClassName(data: str, options: int = 0) -> SBTypeSynthetic: ... + @staticmethod + def CreateWithScriptCode(data: str, options: int = 0) -> SBTypeSynthetic: ... + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def IsClassCode(self) -> bool: ... + def IsClassName(self) -> bool: ... + def GetData(self) -> str: ... + def SetClassName(self, data: str) -> None: ... + def SetClassCode(self, data: str) -> None: ... + def GetOptions(self) -> int: ... + def SetOptions(self, arg2: int) -> None: ... + def GetDescription(self, description: SBStream, description_level: int) -> bool: ... + def IsEqualTo(self, rhs: SBTypeSynthetic) -> bool: ... + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + options: int + contains_code: bool + synthetic_data: str + +class SBUnixSignals: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def Clear(self) -> None: ... + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetSignalAsCString(self, signo: int) -> str: ... + def GetSignalNumberFromName(self, name: str) -> int: ... + def GetShouldSuppress(self, signo: int) -> bool: ... + def SetShouldSuppress(self, signo: int, value: bool) -> bool: ... + def GetShouldStop(self, signo: int) -> bool: ... + def SetShouldStop(self, signo: int, value: bool) -> bool: ... + def GetShouldNotify(self, signo: int) -> bool: ... + def SetShouldNotify(self, signo: int, value: bool) -> bool: ... + def GetNumSignals(self) -> int: ... + def GetSignalAtIndex(self, index: int) -> int: ... + def __iter__(self) -> Iterator[int]: ... + def __len__(self) -> int: ... + def get_unix_signals_list(self) -> Incomplete: ... + threads: list[Incomplete] + +class SBValue: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def Clear(self) -> None: ... + def GetError(self) -> SBError: ... + def GetID(self) -> int: ... + def GetName(self) -> str: ... + def GetTypeName(self) -> str: ... + def GetDisplayTypeName(self) -> str: ... + def GetByteSize(self) -> int: ... + def IsInScope(self) -> bool: ... + def GetFormat(self) -> int: ... + def SetFormat(self, format: int) -> None: ... + def GetValue(self) -> str: ... + def GetValueAsSigned(self, *args: Incomplete) -> int: ... + def GetValueAsUnsigned(self, *args: Incomplete) -> int: ... + def GetValueAsAddress(self) -> int: ... + def GetValueType(self) -> int: ... + def GetValueDidChange(self) -> bool: ... + def GetSummary( + self, stream: Union[SBStream, None] = None, options: Union[SBTypeSummaryOptions, None] = None + ) -> Incomplete: ... + def GetObjectDescription(self) -> str: ... + def GetDynamicValue(self, use_dynamic: int) -> SBValue: ... + def GetStaticValue(self) -> SBValue: ... + def GetNonSyntheticValue(self) -> SBValue: ... + def GetSyntheticValue(self) -> SBValue: ... + def GetPreferDynamicValue(self) -> bool: ... + def SetPreferDynamicValue(self, use_dynamic: bool) -> None: ... + def GetPreferSyntheticValue(self) -> bool: ... + def SetPreferSyntheticValue(self, use_synthetic: bool) -> None: ... + def IsDynamic(self) -> bool: ... + def IsSynthetic(self) -> bool: ... + def IsSyntheticChildrenGenerated(self) -> bool: ... + def SetSyntheticChildrenGenerated(self, arg2: bool) -> None: ... + def GetLocation(self) -> str: ... + def SetValueFromCString(self, value_str: str, error: Union[SBError, None] = None) -> bool: ... + def GetTypeFormat(self) -> SBTypeFormat: ... + def GetTypeSummary(self) -> SBTypeSummary: ... + def GetTypeFilter(self) -> SBTypeFilter: ... + def GetTypeSynthetic(self) -> SBTypeSynthetic: ... + def CreateChildAtOffset(self, name: str, offset: int, type: SBType) -> SBValue: ... + def Cast(self, type: SBType) -> SBValue: ... + def CreateValueFromExpression( + self, name: str, expression: str, options: Union[SBExpressionOptions, None] = None + ) -> SBValue: ... + def CreateValueFromAddress(self, name: str, address: int, type: SBType) -> SBValue: ... + def CreateValueFromData(self, name: str, data: SBData, type: SBType) -> SBValue: ... + def CreateBoolValue(self, name: str, value: bool) -> SBValue: ... + def GetChildAtIndex( + self, idx: int, use_dynamic: Union[int, None] = None, treat_as_array: Union[bool, None] = None + ) -> SBValue: ... + def GetIndexOfChildWithName(self, name: str) -> int: ... + def GetChildMemberWithName(self, name: str, use_dynamic: Union[int, None] = None) -> SBValue: ... + def GetValueForExpressionPath(self, expr_path: str) -> SBValue: ... + def AddressOf(self) -> SBValue: ... + def GetLoadAddress(self) -> int: ... + def GetAddress(self) -> SBAddress: ... + def GetPointeeData(self, item_idx: int = 0, item_count: int = 1) -> SBData: ... + def GetData(self) -> SBData: ... + def SetData(self, data: SBData, error: SBError) -> bool: ... + def Clone(self, new_name: str) -> SBValue: ... + def GetDeclaration(self) -> SBDeclaration: ... + def MightHaveChildren(self) -> bool: ... + def IsRuntimeSupportValue(self) -> bool: ... + def GetNumChildren(self, max: Union[int, None] = None) -> int: ... + def GetOpaqueType(self) -> Incomplete: ... + def GetTarget(self) -> SBTarget: ... + def GetProcess(self) -> SBProcess: ... + def GetThread(self) -> SBThread: ... + def GetFrame(self) -> SBFrame: ... + def Dereference(self) -> SBValue: ... + def TypeIsPointerType(self) -> bool: ... + def GetType(self) -> SBType: ... + def Persist(self) -> SBValue: ... + def GetDescription(self, description: SBStream) -> bool: ... + def GetExpressionPath(self, description: SBStream, qualify_cxx_base_classes: Union[bool, None] = None) -> bool: ... + def EvaluateExpression( + self, expr: str, options: Union[SBExpressionOptions, None] = None, name: Union[str, None] = None + ) -> SBValue: ... + def Watch(self, resolve_loc: bool, read: bool, write: bool, error: Union[SBError, None] = None) -> SBWatchpoint: ... + def WatchPointee(self, resolve_location: bool, read: bool, write: bool, error: SBError) -> SBWatchpoint: ... + def GetVTable(self) -> SBValue: ... + def __get_dynamic__(self) -> Incomplete: ... + + class children_access: + sbvalue: SBValue + def __init__(self, sbvalue: SBValue) -> None: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + + def get_child_access_object(self) -> children_access: ... + valobj: Incomplete + def get_member_access_object(self) -> Incomplete: ... + def get_value_child_list(self) -> list[SBValue]: ... + def __hex__(self) -> Incomplete: ... + def __iter__(self) -> Iterator[SBValue]: ... + def __len__(self) -> int: ... + children: list[SBValue] + child: Incomplete + member: Incomplete + name: str + type: SBType + size: int + is_in_scope: bool + format: int + value: str + value_type: int + changed: bool + data: SBData + load_addr: int + addr: SBAddress + deref: SBValue + address_of: SBValue + error: SBError + summary: str + description: str + dynamic: SBValue + location: str + target: SBTarget + process: SBProcess + thread: SBThread + frame: SBFrame + num_children: int + unsigned: int + signed: int + def get_expr_path(self) -> Incomplete: ... + path: Incomplete + def synthetic_child_from_expression( + self, name: Incomplete, expr: Incomplete, options: Incomplete = None + ) -> Incomplete: ... + def synthetic_child_from_data(self, name: Incomplete, data: Incomplete, type: Incomplete) -> Incomplete: ... + def synthetic_child_from_address(self, name: Incomplete, addr: Incomplete, type: Incomplete) -> Incomplete: ... + def linked_list_iter( + self, next_item_name: Incomplete, end_of_list_test: Incomplete = ... + ) -> Iterator[Incomplete]: ... + +class SBValueList: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def Clear(self) -> None: ... + def Append(self, val: Union[SBValue, SBValueList]) -> None: ... + def GetSize(self) -> int: ... + def GetValueAtIndex(self, idx: int) -> SBValue: ... + def GetFirstValueByName(self, name: str) -> SBValue: ... + def FindValueObjectByUID(self, uid: int) -> SBValue: ... + def GetError(self) -> SBError: ... + def __iter__(self) -> Iterator[SBValue]: ... + def __len__(self) -> int: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + +class SBVariablesOptions: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def IsValid(self) -> bool: ... + def GetIncludeArguments(self) -> bool: ... + def SetIncludeArguments(self, arg2: bool) -> None: ... + def GetIncludeRecognizedArguments(self, arg2: SBTarget) -> bool: ... + def SetIncludeRecognizedArguments(self, arg2: bool) -> None: ... + def GetIncludeLocals(self) -> bool: ... + def SetIncludeLocals(self, arg2: bool) -> None: ... + def GetIncludeStatics(self) -> bool: ... + def SetIncludeStatics(self, arg2: bool) -> None: ... + def GetInScopeOnly(self) -> bool: ... + def SetInScopeOnly(self, arg2: bool) -> None: ... + def GetIncludeRuntimeSupportValues(self) -> bool: ... + def SetIncludeRuntimeSupportValues(self, arg2: bool) -> None: ... + def GetUseDynamic(self) -> int: ... + def SetUseDynamic(self, arg2: int) -> None: ... + +class SBWatchpoint: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def __nonzero__(self) -> bool: ... + __bool__ = __nonzero__ + def __eq__(self, rhs: Any) -> bool: ... + def __ne__(self, rhs: Any) -> bool: ... + def IsValid(self) -> bool: ... + def GetError(self) -> SBError: ... + def GetID(self) -> int: ... + def GetHardwareIndex(self) -> int: ... + def GetWatchAddress(self) -> int: ... + def GetWatchSize(self) -> int: ... + def SetEnabled(self, enabled: bool) -> None: ... + def IsEnabled(self) -> bool: ... + def GetHitCount(self) -> int: ... + def GetIgnoreCount(self) -> int: ... + def SetIgnoreCount(self, n: int) -> None: ... + def GetCondition(self) -> str: ... + def SetCondition(self, condition: str) -> None: ... + def GetDescription(self, description: SBStream, level: int) -> bool: ... + def Clear(self) -> None: ... + @staticmethod + def EventIsWatchpointEvent(event: SBEvent) -> bool: ... + @staticmethod + def GetWatchpointEventTypeFromEvent(event: SBEvent) -> int: ... + @staticmethod + def GetWatchpointFromEvent(event: SBEvent) -> SBWatchpoint: ... + def GetType(self) -> SBType: ... + def GetWatchValueKind(self) -> int: ... + def GetWatchSpec(self) -> str: ... + def IsWatchingReads(self) -> bool: ... + def IsWatchingWrites(self) -> bool: ... + def __hex__(self) -> Incomplete: ... + def __len__(self) -> int: ... + +class SBWatchpointOptions: + thisown: Incomplete + def __init__(self, *args: Incomplete) -> None: ... + __swig_destroy__: Incomplete + def SetWatchpointTypeRead(self, read: bool) -> None: ... + def GetWatchpointTypeRead(self) -> bool: ... + def SetWatchpointTypeWrite(self, write_type: int) -> None: ... + def GetWatchpointTypeWrite(self) -> int: ... + +def in_range(symbol: SBSymbol, section: SBSection) -> bool: ... +def command(command_name: Incomplete = None, doc: Incomplete = None) -> Incomplete: ... + +class declaration: + file: Incomplete + line: Incomplete + col: Incomplete + def __init__(self, file: Incomplete, line: int, col: int) -> None: ... + +class value_iter: + def __iter__(self) -> Incomplete: ... + def __next__(self) -> Incomplete: ... + def next(self) -> Incomplete: ... + def __eq__(self, other: Any) -> bool: ... + def __len__(self) -> int: ... + index: int + length: int + sbvalue: SBValue + def __init__(self, value: Incomplete) -> None: ... + +class value: + sbvalue: SBValue + def __init__(self, sbvalue: SBValue) -> None: ... + def __nonzero__(self) -> bool: ... + def __bool__(self) -> bool: ... + def __getitem__(self, key: Incomplete) -> Incomplete: ... + def __iter__(self) -> Incomplete: ... + def __getattr__(self, name: Incomplete) -> Incomplete: ... + def __add__(self, other: Incomplete) -> Incomplete: ... + def __sub__(self, other: Incomplete) -> Incomplete: ... + def __mul__(self, other: Incomplete) -> Incomplete: ... + def __floordiv__(self, other: Incomplete) -> Incomplete: ... + def __mod__(self, other: Incomplete) -> Incomplete: ... + def __divmod__(self, other: Incomplete) -> Incomplete: ... + def __pow__(self, other: Incomplete) -> Incomplete: ... + def __lshift__(self, other: Incomplete) -> Incomplete: ... + def __rshift__(self, other: Incomplete) -> Incomplete: ... + def __and__(self, other: Incomplete) -> Incomplete: ... + def __xor__(self, other: Incomplete) -> Incomplete: ... + def __or__(self, other: Incomplete) -> Incomplete: ... + def __div__(self, other: Incomplete) -> Incomplete: ... + def __truediv__(self, other: Incomplete) -> Incomplete: ... + def __iadd__(self, other: Incomplete) -> Incomplete: ... + def __isub__(self, other: Incomplete) -> Incomplete: ... + def __imul__(self, other: Incomplete) -> Incomplete: ... + def __idiv__(self, other: Incomplete) -> Incomplete: ... + def __itruediv__(self, other: Incomplete) -> Incomplete: ... + def __ifloordiv__(self, other: Incomplete) -> Incomplete: ... + def __imod__(self, other: Incomplete) -> Incomplete: ... + def __ipow__(self, other: Incomplete) -> Incomplete: ... + def __ilshift__(self, other: Incomplete) -> Incomplete: ... + def __irshift__(self, other: Incomplete) -> Incomplete: ... + def __iand__(self, other: Incomplete) -> Incomplete: ... + def __ixor__(self, other: Incomplete) -> Incomplete: ... + def __ior__(self, other: Incomplete) -> Incomplete: ... + def __neg__(self) -> Incomplete: ... + def __pos__(self) -> Incomplete: ... + def __abs__(self) -> Incomplete: ... + def __invert__(self) -> Incomplete: ... + def __complex__(self) -> complex: ... + def __int__(self) -> int: ... + def __long__(self) -> int: ... + def __float__(self) -> float: ... + def __oct__(self) -> Incomplete: ... + def __hex__(self) -> Incomplete: ... + def __len__(self) -> int: ... + def __eq__(self, other: Any) -> bool: ... + def __ne__(self, other: Any) -> bool: ... + +class SBSyntheticValueProvider: + def __init__(self, valobj: Incomplete) -> None: ... + def num_children(self) -> int: ... + def get_child_index(self, name: Incomplete) -> None: ... + def get_child_at_index(self, idx: int) -> None: ... + def update(self) -> None: ... + def has_children(self) -> Incomplete: ... + def __len__(self) -> int: ... + def __iter__(self) -> Incomplete: ... + +def is_numeric_type(basic_type: Incomplete) -> Incomplete: ... + +debugger_unique_id: int +debugger: Incomplete +target: Incomplete +process: Incomplete +thread: Incomplete +frame: Incomplete