def rbtree_postorder_for_each(root: gdb.Value) -> Iterable[gdb.Value]: """ Iterate over nodes of a rooted RB tree in post-order fashion Args: root: The tree to iterate. The value must be of type ``struct rb_root`` or ``struct rb_root *``. Yields: gdb.Value: The next node of the tree. The value is of type ``struct rb_node``. Raises: :obj:`.CorruptTreeError`: the list is corrupted """ if not isinstance(root, gdb.Value): raise ArgumentTypeError('root', root, gdb.Value) if root.type == types.rb_root_type.pointer(): root = root.dereference() elif root.type != types.rb_root_type: raise UnexpectedGDBTypeError('root', root, types.rb_root_type) if root.type is not types.rb_root_type: types.override('struct rb_root', root.type) if int(root.address) == 0: raise CorruptTreeError("root is NULL pointer") node = _rb_left_deepest_node(root['rb_node']) while node is not None: yield node.dereference() node = _rb_next_postorder(node)
def decode_uuid_t(value: gdb.Value) -> uuid.UUID: """ Decode a Linux kernel uuid_t into a Python-style UUID object Args: value (gdb.Value): The uuid_t to be decoded Returns: uuid.UUID: The UUID object that describes the value Raises: TypeError: value is not gdb.Value<uuid_t> """ if not isinstance(value, gdb.Value): raise TypeError("value must be gdb.Value") if value.type != types.uuid_t_type: if (value.type.code == gdb.TYPE_CODE_PTR and value.type.target() == types.uuid_t_type): value = value.dereference() else: raise TypeError("value must describe a uuid_t") if struct_has_member(types.uuid_t_type, 'b'): member = 'b' else: member = '__u_bits' return decode_uuid(value[member])
def klist_for_each(klist: gdb.Value) -> Iterable[gdb.Value]: """ Iterate over a klist and yield each node Args: klist: The list to iterate. The value must be of type ``struct klist`` or ``struct klist *``. Yields: :obj:`gdb.Value`: The next node in the list. The value is of type ``struct klist_node``. """ if klist.type == types.klist_type.pointer(): klist = klist.dereference() elif klist.type != types.klist_type: raise InvalidArgumentError( "klist must be gdb.Value representing 'struct klist' or 'struct klist *' not {}" .format(klist.type)) if klist.type is not types.klist_type: types.override('struct klist', klist.type) for node in list_for_each_entry(klist['k_list'], types.klist_node_type, 'n_node'): if node['n_klist'] != klist.address: raise KlistCorruptedError("Corrupted") yield node
def gendisk_name(gendisk: gdb.Value) -> str: """ Returns the name of the provided block device. This method evaluates the block device and returns the name, including partition number, if applicable. Args: gendisk: A ``struct gendisk`` or ``struct hd_struct`` for which to return the name. The value must be of type ``struct gendisk`` or ``struct hd_struct``. Returns: :obj:`str`: The name of the block device Raises: :obj:`.InvalidArgumentError`: gendisk does not describe a ``struct gendisk`` or ``struct hd_struct`` """ if gendisk.type.code == gdb.TYPE_CODE_PTR: gendisk = gendisk.dereference() if get_basic_type(gendisk.type) == types.gendisk_type: return gendisk['disk_name'].string() if get_basic_type(gendisk.type) == types.hd_struct_type: parent = dev_to_gendisk(part_to_dev(gendisk)['parent']) return "{}{:d}".format(gendisk_name(parent), int(gendisk['partno'])) raise InvalidArgumentError("expected {} or {}, not {}".format( types.gendisk_type, types.hd_struct_type, gendisk.type.unqualified()))
def container_of(val: gdb.Value, gdbtype: gdb.Type, member: str) -> gdb.Value: """ Returns an object that contains the specified object at the given offset. Args: val (gdb.Value): The value to be converted. It can refer to an allocated structure or a pointer. gdbtype (gdb.Type): The type of the object that will be generated member (str): The name of the member in the target struct that contains `val`. Returns: gdb.Value<gdbtype>: The converted object, of the type specified by the caller. Raises: TypeError: val is not a gdb.Value """ if not isinstance(val, gdb.Value): raise ArgumentTypeError('val', val, gdb.Value) if not isinstance(gdbtype, gdb.Type): raise ArgumentTypeError('gdbtype', gdbtype, gdb.Type) charp = types.char_p_type if val.type.code != gdb.TYPE_CODE_PTR: val = val.address offset = offsetof(gdbtype, member) return (val.cast(charp) - offset).cast(gdbtype.pointer()).dereference()
def make_node_iterator(node_ptr: gdb.Value): """ """ # Yield nodes, from left to right while node_ptr != 0: node = node_ptr.dereference() node_ptr = node['next'] yield node['data']
def _to_ast_ptr(value: gdb.Value) -> gdb.Value: if value.type.code == gdb.TYPE_CODE_PTR: if value.type.target().name == "ast_t": return value else: return _to_ast_ptr(value.dereference()) elif value.type.code == gdb.TYPE_CODE_TYPEDEF and value.type.name == "ast_ptr_t": return value raise ValueError
def print_icolumn_type(col: gdb.Value) -> str: obj_type: gdb.Type = col.dynamic_type obj_casted: gdb.Value = col.cast(obj_type) if obj_type == t_col_array: offsets: gdb.Value = gdb.dereference(obj_casted["offsets"]).cast(t_col_offsets)["data"] start, end, _= get_podarray_bounds(offsets) nested_printed: str = print_icolumn_type(gdb.dereference(obj_casted["data"])) return "ColumnArray[size: {}, {}]".format(end - start, nested_printed) elif obj_type == t_col_const: data: gdb.Value = gdb.dereference(obj_casted["data"]) nested_printed: str = print_icolumn_type(data) return "ColumnConst[size: {}, {}]".format(obj_casted["s"], nested_printed) elif obj_type == t_col_lc: dictionary: gdb.Value = obj_casted["dictionary"] index: gdb.Value = obj_casted["idx"] size_of_index_type: int = int(index["size_of_type"]) index_type: str = "UInt8" if size_of_index_type == t_uint16.sizeof: index_type = "UInt16" elif size_of_index_type == t_uint32.sizeof: index_type = "UInt32" else: index_type = "UInt64" nested_col: gdb.Value = gdb.dereference( gdb.dereference(dictionary["column_unique"])["column_holder"]) nested_printed: str = print_icolumn_type(nested_col) return "ColumnLowCardinality[index: {}, {}]".format(index_type, nested_printed) elif obj_type == t_col_nullable: col_nested: gdb.Value = gdb.dereference(obj_casted["nested_column"]) nested_printed: str = print_icolumn_type(col_nested) return "ColumnNullable[{}]".format(nested_printed) elif obj_type == t_col_vector: nested_type = obj_casted.template_argument(0) start, end, _ = get_podarray_bounds(obj_casted["data"]) return "ColumnArray[size: {}, {}]".format(nested_type, end - start) else: return "NI {}".format(obj_type)
def invoke(self, obj: gdb.Value, type: gdb.Value) -> gdb.Value: try: type_string = type.string() except gdb.error: raise gdb.error('the second argument must be literal string') try: dispatch = self._dispatch[parse_type(obj)] except KeyError: raise gdb.error( 'the first argument must be of supported types: interface, literal address, void*' ) return dispatch(obj, type_string)
def show_one_mount(self, mnt: gdb.Value, args: argparse.Namespace) -> None: if mnt.type.code == gdb.TYPE_CODE_PTR: mnt = mnt.dereference() flags = "" if args.f: flags = " ({})".format(mount_flags(mnt)) path = d_path(mnt, mount_root(mnt)) if args.v: print("{:016x} {:016x} {:<10} {:<16} {}" .format(int(mnt.address), int(mount_super(mnt)), mount_fstype(mnt), mount_device(mnt), path)) else: print("{} on {} type {}{}" .format(mount_device(mnt), path, mount_fstype(mnt), flags))
def children(self): start = int(self.data_ptr) & ~1 hashes = self.hash_uint_size * self.capacity align = self.pair_type_size len_rounded_up = (((( (hashes + align) % self.modulo - 1) % self.modulo) & ~( (align - 1) % self.modulo)) % self.modulo - hashes) % self.modulo pairs_offset = hashes + len_rounded_up pairs_start = Value(start + pairs_offset).cast( self.pair_type.pointer()) for index in range(self.size): table_index = self.valid_indices[index] idx = table_index & self.capacity_mask element = (pairs_start + idx).dereference() if self.show_values: yield "key{}".format(index), element[ZERO_FIELD] yield "val{}".format(index), element[FIRST_FIELD] else: yield "[{}]".format(index), element[ZERO_FIELD]
def __call__(self, value: gdb.Value): """ """ # Get basic typename typename = self.get_basic_type(value.type) if typename is None: return None # Match against lookup regex match = self.lookup_regex.match(typename) if match is None: return None if value.type.code == gdb.TYPE_CODE_REF: if hasattr(gdb.Value, 'referenced_value'): value = value.referenced_value() # Match against lookup table try: basename = match.group(1) subprinter = self.lookup_table[basename] return subprinter(value) except KeyError: # Printer not found, return None return None
def cast_interface(obj: gdb.Value, type: str) -> gdb.Value: gdb_type = gdb.lookup_type(type) return obj.cast(gdb_type)
def cast_address(obj: gdb.Value, type: str) -> gdb.Value: gdb_type = gdb.lookup_type(type).pointer() return obj.cast(gdb_type)
def list_for_each(list_head: gdb.Value, include_head: bool = False, reverse: bool = False, print_broken_links: bool = True, exact_cycles: bool = False) -> Iterator[gdb.Value]: """ Iterate over a list and yield each node Args: list_head: The list to iterate. The value must be of type ``struct list_head`` or ``struct list_head *``. include_head (optional): Include the head of the list in iteration - useful for lists with no anchors reverse (optional): Iterate the list in reverse order (follow the ``prev`` links) print_broken_links (optional): Print warnings about broken links exact_cycles (optional): Detect and raise an exception if a cycle is detected in the list Yields: gdb.Value: The next node in the list. The value is of type ``struct list_head``. Raises: :obj:`.CorruptListError`: the list is corrupted :obj:`.ListCycleError`: the list contains cycles :obj:`BufferError`: portions of the list cannot be read :obj:`gdb.NotAvailableError`: The target value is not available. """ pending_exception = None if not isinstance(list_head, gdb.Value): raise ArgumentTypeError('list_head', list_head, gdb.Value) if list_head.type == types.list_head_type.pointer(): list_head = list_head.dereference() elif list_head.type != types.list_head_type: raise UnexpectedGDBTypeError('list_head', list_head, types.list_head_type) if list_head.type is not types.list_head_type: types.override('struct list_head', list_head.type) fast = None if int(list_head.address) == 0: raise CorruptListError("list_head is NULL pointer.") next_ = 'next' prev_ = 'prev' if reverse: next_ = 'prev' prev_ = 'next' if exact_cycles: visited: Set[int] = set() if include_head: yield list_head try: nxt = list_head[next_] prev = list_head if int(nxt) == 0: raise CorruptListError("{} pointer is NULL".format(next_)) node = nxt.dereference() except gdb.error as e: raise BufferError("Failed to read list_head {:#x}: {}".format( int(list_head.address), str(e))) from e last_good_addr = None while node.address != list_head.address: if exact_cycles: if int(node.address) in visited: raise ListCycleError("Cycle in list detected.") visited.add(int(node.address)) try: if int(prev.address) != int(node[prev_]): error = f"broken {prev_} link {int(prev.address):#x} " error += f"-{next_}-> {int(node.address):#x} " error += f"-{prev_}-> {int(node[prev_]):#x}" pending_exception = CorruptListError(error) if print_broken_links: print(error) # broken prev link means there might be a cycle that # does not include the initial head, so start detecting # cycles if not exact_cycles and fast is None: fast = node nxt = node[next_] # only yield after trying to read something from the node, no # point in giving out bogus list elements yield node except gdb.error as e: if last_good_addr is not None: last_good_str = f"0x{last_good_addr:x}" else: last_good_str = "(none)" raise BufferError( f"Failed to read list_head 0x{int(node.address):x} " f"in list 0x{int(list_head.address):x}, last good " f"list_head {last_good_str}: {str(e)}") from e try: if fast is not None: # are we detecting cycles? advance fast 2 times and compare # each with our current node (Floyd's Tortoise and Hare # algorithm) for i in range(2): # pylint: disable=unused-variable fast = fast[next_].dereference() if int(node.address) == int(fast.address): raise ListCycleError("Cycle in list detected.") except gdb.error: # we hit an unreadable element, so just stop detecting cycles # and the slow iterator will hit it as well fast = None prev = node if int(nxt) == 0: raise CorruptListError("{} -> {} pointer is NULL".format( node.address, next_)) last_good_addr = int(node.address) node = nxt.dereference() if pending_exception is not None: # The pylint error seems to think we'll raise None here raise pending_exception # pylint: disable=raising-bad-type
def string_value(self, value: gdb.Value) -> str: return value.string()
def pointer_to_ast(self, pointer: gdb.Value) -> gdb.Value: return pointer.cast(gdb.lookup_type("ast_t").pointer())