def _extract_type(type_: gdb.Type) -> Type: """ Extract a gdb.Type """ fields = [extract_field(f) for f in type_.fields()] template_arguments = [] try: i = 0 while True: arg = type_.template_argument(i) if isinstance(arg, gdb.Value): arg = _extract_value(arg) elif isinstance(arg, gdb.Type): arg = _extract_type(arg) else: raise ValueError("Template argument is not a gdb.Value or gdb.Type?") template_arguments.append(arg) i += 1 except gdb.error: pass return Type(type_code=type_.code, name=type_.name, size=type_.sizeof, tag=type_.tag, fields=fields, template_arguments=template_arguments )
def get_basic_type(type: gdb.Type): """ """ if type.code == gdb.TYPE_CODE_REF: # Dereference type type = type.target() # Get unqualified type, strip typedefs type = type.unqualified().strip_typedefs() return type.tag
def _format_struct(self, type: gdb.Type, force_name: str = None) -> Tuple[str, str]: if utils.get_type_name_or_tag(type) is not None and force_name is None: # Named types are not expanded since they are declared before this type return self.get_type_name(utils.get_type_name_or_tag(type)), "" else: if force_name is not None: # Named types are always written as structs since they need to be subclassable out = "struct" else: if type.code == gdb.TYPE_CODE_UNION: out = "union" else: out = "struct" if force_name is not None: out += " " + force_name out += " {\n" for f in type.fields(): pre, post = self._format_type(f.type) out += "\n".join(" " + x for x in pre.splitlines()) out += (" " + f.name if f.name is not None else "") + post + ";\n" out += "}" return out, ""
def _process_type(self, parent_type: TypeWrapper, t: gdb.Type, agg: TypeAggregator, process_fields: bool): t = t.strip_typedefs() if t.code == gdb.TYPE_CODE_PTR or t.code == gdb.TYPE_CODE_REF: basic = utils.get_basic_type(t) if basic.code == gdb.TYPE_CODE_FUNC: self._process_type(parent_type, basic.target(), agg, False) self._process_type_fields(parent_type, basic, agg) else: basic = utils.get_struct_type(t) if basic is not None: agg.add_work_item(basic) return t = utils.get_struct_type(t) if t is None: # Not a struct type return if process_fields or utils.get_type_name_or_tag(t) is None: # Unnamed types are not added to the type graph self._process_type_fields(parent_type, t, agg) else: field_wrapper = agg.add_work_item(t) agg.add_type_reference(field_wrapper, parent_type)
def setup_page_type(cls, gdbtype: gdb.Type) -> None: # TODO: should check config, but that failed to work on ppc64, hardcode # 64k for now if crash.current_target().arch.name() == "powerpc:common64": cls.PAGE_SHIFT = 16 # also a config cls.directmap_base = 0xc000000000000000 cls.sparsemem = True cls.sparsemem_vmemmap = False cls.sparsemem_extreme = False cls.SECTION_SIZE_BITS = 24 cls.PAGE_SIZE = 1 << cls.PAGE_SHIFT cls.PAGE_MASK = ~(cls.PAGE_SIZE - 1) cls.slab_cache_name = find_member_variant(gdbtype, ['slab_cache', 'lru']) cls.slab_page_name = find_member_variant(gdbtype, ['slab_page', 'lru']) cls.compound_head_name = find_member_variant( gdbtype, ['compound_head', 'first_page']) if not hasattr(cls, 'vmemmap'): cls.vmemmap = gdb.Value(cls.vmemmap_base).cast(gdbtype.pointer()) if struct_has_member(gdbtype, 'page_type'): cls._is_buddy = cls.__is_buddy_page_type else: cls._is_buddy = cls.__is_buddy_mapcount cls.setup_page_type_done = True if cls.setup_pageflags_done and not cls.setup_pageflags_finish_done: cls.setup_pageflags_finish()
def _is_ast(type_: gdb.Type) -> bool: if type_.code == gdb.TYPE_CODE_PTR: target = type_.target() return target.name == "ast_t" or _is_ast(target) elif type_.code == gdb.TYPE_CODE_TYPEDEF: return type_.name == "ast_ptr_t" return False
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 _export_req_flags(req_flag_bits: gdb.Type) -> None: for (name, field) in req_flag_bits.items(): globals()[name[2:]] = 1 << field.enumval # Define to 0 flags that don't exist. for name in ['REQ_PREFLUSH', 'REQ_FLUSH']: if not name in globals(): globals()[name] = 0
def make_template_arguments_iterator(type: gdb.Type): """ """ try: idx = 0 while True: yield type.template_argument(idx) idx += 1 except RuntimeError: pass
def setup_pageflags(cls, gdbtype: gdb.Type) -> None: for field in gdbtype.fields(): cls.pageflags[field.name] = field.enumval cls.setup_pageflags_done = True if cls.setup_page_type_done and not cls.setup_pageflags_finish_done: cls.setup_pageflags_finish() cls.PG_slab = 1 << cls.pageflags['PG_slab'] cls.PG_lru = 1 << cls.pageflags['PG_lru']
def __populate_names(cls, enum_type: gdb.Type, items_name: str) -> Tuple[int, List[str]]: nr_items = enum_type[items_name].enumval names = ["__UNKNOWN__"] * nr_items for field in enum_type.fields(): if field.enumval < nr_items: names[field.enumval] = field.name return (nr_items, names)
def _format_type(self, type: gdb.Type, force_name: str = None) -> Tuple[str, str]: if type.code == gdb.TYPE_CODE_PTR: target_pre, target_post = self._format_type(type.target()) return target_pre + "*", target_post elif type.code == gdb.TYPE_CODE_ARRAY: base = type.target() size = int(type.sizeof / base.sizeof) target_pre, target_post = self._format_type(type.target()) return target_pre, target_post + "[" + str(size) + "]" elif type.code == gdb.TYPE_CODE_STRUCT or type.code == gdb.TYPE_CODE_UNION: return self._format_struct(type, force_name) elif type.code == gdb.TYPE_CODE_TYPEDEF: return self._format_type(type.target(), force_name) elif type.code == gdb.TYPE_CODE_FUNC: pre = "".join(self._format_type(type.target())) + "(" arglist = type.fields() arglist_str = ", ".join("".join(self._format_type(x.type)) for x in arglist) return pre, ")(" + arglist_str + ")" else: return str(type), ""
def get_basic_type(type_: gdb.Type) -> gdb.Type: """Return the "basic" type of a type. Arguments: type_: The type to reduce to its basic type. Returns: type_ with const/volatile is stripped away, and typedefs/references converted to the underlying type. """ while (type_.code == gdb.TYPE_CODE_REF or type_.code == gdb.TYPE_CODE_RVALUE_REF or type_.code == gdb.TYPE_CODE_TYPEDEF or type_.code == gdb.TYPE_CODE_PTR): if (type_.code == gdb.TYPE_CODE_REF or type_.code == gdb.TYPE_CODE_RVALUE_REF or type_.code == gdb.TYPE_CODE_PTR): type_ = type_.target() else: type_ = type_.strip_typedefs() return type_.unqualified()
def get_struct_type(t: gdb.Type) -> Optional[gdb.Type]: """Return the "basic" type of a type. Arguments: type_: The type to reduce to its basic type. Returns: type_ with const/volatile is stripped away, and typedefs/references converted to the underlying type. """ t = t.strip_typedefs() while True: if t.code == gdb.TYPE_CODE_PTR or t.code == gdb.TYPE_CODE_REF or t.code == gdb.TYPE_CODE_RVALUE_REF: t = t.target() elif t.code == gdb.TYPE_CODE_ARRAY: t = t.target() elif t.code == gdb.TYPE_CODE_TYPEDEF: t = t.target() else: break if t.code != gdb.TYPE_CODE_STRUCT and t.code != gdb.TYPE_CODE_UNION: return None else: return t while (type_.code == gdb.TYPE_CODE_REF or type_.code == gdb.TYPE_CODE_RVALUE_REF or type_.code == gdb.TYPE_CODE_TYPEDEF or type_.code == gdb.TYPE_CODE_PTR): if (type_.code == gdb.TYPE_CODE_REF or type_.code == gdb.TYPE_CODE_RVALUE_REF or type_.code == gdb.TYPE_CODE_PTR): type_ = type_.target() else: type_ = type_.strip_typedefs() return type_.unqualified()
def all_type_tags(gdb_type: gdb.Type, names=None, ops=None, seen=None) -> set: name_list = list(names or []) if isinstance(names, set) else names or [] seen = seen or set([]) ops = ops or [] prev_type = None while prev_type != gdb_type: prev_type = gdb_type gdb_type = gdb_type.unqualified() if str(gdb_type) in seen: break seen.add(str(gdb_type)) if gdb_type.tag is not None: print(" .. : <tag=%s>" % str(gdb_type.tag)) name_list.append(gdb_type.tag) if gdb_type.name is not None: print(" .. : <name=%s>" % str(gdb_type.name)) name_list.append(gdb_type.name) if gdb_type.code in \ (gdb.TYPE_CODE_PTR, gdb.TYPE_CODE_REF, gdb.TYPE_CODE_RVALUE_REF, gdb.TYPE_CODE_ARRAY): print(" .. [%s].target() -> %s" % (str(gdb_type), str(gdb_type.target()))) gdb_type = gdb_type.target() ops.append(gdb.Value.referenced_value) elif gdb_type.code in \ (gdb.TYPE_CODE_UNION, gdb.TYPE_CODE_STRUCT): next_base = next = None for field in gdb_type.fields(): if field.type in (prev_type, gdb_type, None if prev_type is None else prev_type.pointer(), gdb_type.pointer()): continue if field.is_base_class: print( f" [%s].{field.name} ***BASE_CLASS*** {str(field.type)}"\ % (str(field.parent_type))) all_type_tags( field.type, names=name_list, ops=(ops + [(lambda v: v.cast(field.parent_type)[field])]), seen=seen) elif gdb_type.code == gdb.TYPE_CODE_TYPEDEF: print(" [%s] typedef -> %s" % (str(gdb_type), str(gdb_type.strip_typedefs()))) ops.append(lambda v: v.cast(v.type.strip_typedefs())) gdb_type = gdb_type.strip_typedefs() return (set(name_list), ops)
def get_minsymbol_pointer(symname: str, gdbtype: gdb.Type) -> gdb.Value: """ Looks up a minsymbol by name and returns a typed pointer to it. Args: symname (str): The name of minsymbol to lookup. gdbtype (gdb.Type): The type of the minsymbol. Returns: gdb.Value: The pointer of given type to the minsymbol. Raises: MissingSymbolError: The minsymbol cannot be located """ sym = gdb.lookup_minimal_symbol(symname) if not sym: raise MissingSymbolError(f"Cannot locate minsymbol {symname}") addr = sym.value().address return addr.cast(gdbtype.pointer())
def get_typed_pointer(val: AddressSpecifier, gdbtype: gdb.Type) -> gdb.Value: """ Returns a pointer to the requested type at the given address If the val is passed as a gdb.Value, it will be casted to the expected type. If it is not a pointer, the address of the value will be used instead. Args: val (gdb.Value, str, or int): The address for which to provide a casted pointer gdbtype (gdb.Type): The type of the pointer to return Returns: gdb.Value: The casted pointer of the requested type Raises: TypeError: string value for val does not describe a hex address or the type cannot be converted to an address """ if gdbtype.code != gdb.TYPE_CODE_PTR: gdbtype = gdbtype.pointer() if isinstance(val, gdb.Value): if val.type.code != gdb.TYPE_CODE_PTR: val = val.address elif isinstance(val, str): try: val = int(val, 16) except TypeError as e: raise TypeError( "string must describe hex address: {}".format(e)) from None else: val = int(val) if isinstance(val, int): ret = gdb.Value(val).cast(gdbtype) elif isinstance(val, gdb.Value): ret = val.cast(gdbtype) else: raise TypeError(f"val is unexpected type {type(val)}") return ret
def setup_pageflags(cls, gdbtype: gdb.Type) -> None: for field in gdbtype.fields(): cls.pageflags[field.name] = field.enumval cls.setup_pageflags_done = True if cls.setup_page_type_done and not cls.setup_pageflags_finish_done: cls.setup_pageflags_finish() cls.PG_slab = 1 << cls.pageflags['PG_slab'] cls.PG_lru = 1 << cls.pageflags['PG_lru'] cls.PG_head = 1 << cls.pageflags['PG_head'] cls.PG_swapbacked = 1 << cls.pageflags['PG_swapbacked'] flags_check = 0 for flagname in _PAGE_FLAGS_CHECK_AT_FREE: flags_check |= 1 << cls.pageflags[flagname] cls.PAGE_FLAGS_CHECK_AT_FREE = flags_check cls.NR_PAGEFLAGS = cls.pageflags["__NR_PAGEFLAGS"] cls.PAGE_FLAGS_CHECK_AT_PREP = (1 << cls.NR_PAGEFLAGS) - 1
def offsetof_type(gdbtype: gdb.Type, member_name: str, error: bool = True) -> Union[Tuple[int, gdb.Type], None]: """ Returns the offset and type of a named member of a structure Args: gdbtype (gdb.Type): The type that contains the specified member, must be a struct or union member_name (str): The member of the member to resolve error (bool, optional, default=True): Whether to consider lookup failures an error Returns: Tuple of: int: The offset of the resolved member gdb.Type: The type of the resolved member Raises: ArgumentTypeError: gdbtype is not of type gdb.Type InvalidComponentError: member_name is not valid for the type """ if not isinstance(gdbtype, gdb.Type): raise ArgumentTypeError('gdbtype', gdbtype, gdb.Type) # We'll be friendly and accept pointers as the initial type if gdbtype.code == gdb.TYPE_CODE_PTR: gdbtype = gdbtype.target() if gdbtype.code != gdb.TYPE_CODE_STRUCT and \ gdbtype.code != gdb.TYPE_CODE_UNION: raise NotStructOrUnionError('gdbtype', gdbtype) try: return __offsetof(gdbtype, member_name, error) except _InvalidComponentBaseError as e: if error: raise InvalidComponentError(gdbtype, member_name, str(e)) from e return None
def _process_type_fields(self, parent_type: TypeWrapper, t: gdb.Type, agg: TypeAggregator): for f in t.fields(): field_t: gdb.Type = f.type self._process_type(parent_type, field_t, agg, f.name is None)
def can_handle(gdb_type: gdb.Type) -> bool: return str(gdb_type).startswith("std::vector") and str( gdb_type.template_argument(0)) == "bool"
def setup_page_ext_flags(cls, gdbtype: gdb.Type) -> None: for field in gdbtype.fields(): cls.page_ext_flags[field.name] = field.enumval
def callback(self, value: gdb.Type) -> None: if self.pointer: value = value.pointer() self.value = value