使用python语言扩展gdb命令 - 获取类型定义相关信息

331 阅读7分钟

使用python语言扩展gdb命令

概述

gdb 官方提供的命令有时候不能满足我们的需要,或者需要定义一些额外的命令打印一些通用分析信息,这个时候就需要扩展一下gdb,本文提供使用python脚本扩展gdb的方式

本文参考的官方文档:官方文档传送门

环境准备

  1. 首先,看下当前环境上安装的gdb版本,然后下载对应的gdb版本源码。gdb源码传送门
  2. 将源码解压,将其中的gdb/python文件夹拷贝到pycharm的python项目中,本步骤的目的是为了方便后续的代码开发对照, gdb相关的类封装都在python文件夹下的.c里面, 例如gdb.Type对象的定义在py-type.c中。
  3. python 扩展工具代码完成后, 进入gdb, 执行source /home/xxx/tools.py, 即可使用扩展的内容. 官方文档
  4. 设置自动加载 Python Auto-loading (Debugging with GDB) (sourceware.org)

常用方法

基础方法

  • 常用的gdb.parse_and_eval(expression [, global_context]) 解析表达式(必须是字符串)作为当前语言中的表达式,对其求值,并将结果作为gdb.Value返回。如果提供了Global_context,则是一个布尔值,指示是否应该在全局上下文中进行解析。默认值为“False”,表示应该使用当前框架或当前静态上下文。这个函数在实现一个新命令时很有用(参见Python中的CLI命令,参见Python中的GDB/MI命令),因为它提供了一种将命令的参数解析为表达式的方法。简单地计算值也很有用。
  • gdb.execute(command [, from_tty [, to_string]]) 将command(字符串)作为GDB CLI命令求值。如果在命令运行时发生GDB异常,则按照异常处理中的描述进行转换。from_tty标志指定GDB是否应该将此命令视为来自交互调用它的用户。必须是一个布尔值。如果省略,则默认为False。默认情况下,命令产生的任何输出都被发送到GDB的标准输出(如果打开了日志记录,则发送到日志输出)。如果to_string参数为True,那么输出将被gdb收集。执行并作为字符串返回。默认为False,在这种情况下返回值为None。如果to_string为True, GDB虚拟终端将被临时设置为无限的宽度和高度,并且其分页将被禁用;

获取类型

源码定义在py-type.c

获取一个gdb.Type对象

使用gdb.lookup_type(name [, block])获取一个类型对象

  • name: 类型名,str类型。例如:enum Color
  • block: 如果给定了block,则在该作用域中查找name。否则将在全局搜索。

通常,该函数将返回一个gdb.Type的实例。如果找不到指定类型,它将抛出异常。

常用属性

  • alignof: 该类型的对齐方式,以字节为单位。类型对齐来自调试信息;如果没有指定,那么GDB将使用相关的ABI来尝试确定对齐方式。在某些情况下,甚至这也是不可能的,并且将返回零。
  • code: 该类型的类型code。code在
  • name: 该类型的名称。如果此类型没有名称,则返回None。
  • sizeof: 该类型的大小,以目标字符单位表示。通常,目标的char类型是一个8位字节。然而,在一些不寻常的平台上,此类型可能具有不同的大小。动态类型可能没有固定的大小;在这种情况下,该属性的值将为None。
  • tag: 此类型的标记名。标记名是C和c++中struct、union和enum后面的名称,并不是所有的语言都有这个概念。如果该类型没有标记名,则返回None。例如: enum Color类型的标记名是Color
  • objfile: 定义该类型的Objfile,如果没有关联的Objfile则为None。

常用方法

  • range(): 返回一个包含两个元素的Python Tuple对象:参数类型的下界和该类型的上界。如果类型没有范围,GDB将引发一个GDB异常. 通过数组类型.fields[0].range()可以获取数组的范围. 数组长度为类型的上界+1,数组长度是0或者1时,上界都是0,可以进一步根据field.sizeof确定数组长度.sizeof为0时,代表0长;sizeof不为0时,代表数组长度是1.
  • fields(): 返回该类型的字段。行为取决于类型代码: 对于结构和联合类型,此方法返回字段;枚举类型每个枚举常量有一个字段;函数和方法类型每个参数有一个字段;c++类的基类型也表示为字段;数组类型有一个字段表示数组的范围。如果类型不适合这些类别之一,则会引发TypeError。
  • target(): 对于指针类型,目标类型是指向对象的类型。对于数组类型(指类c数组),目标类型是数组元素的类型。对于函数或方法类型,目标类型是返回值的类型。对于复杂类型,目标类型是元素的类型。对于typedef,目标类型是别名类型。如果类型没有target,则此方法将抛出异常。
  • strip_typedefs(): 返回typedef的type类型表示的实际类型的gdb.Type对象

其他工具方法

官网链接: gdb.types (Debugging with GDB) (sourceware.org)

  • make_enum_dict: 返回由enum_type生成的Python字典类型;key为枚举常量名,value为枚举值。例如print(gdb.types.make_enum_dict(gdb.lookup_type("enum ColorEnum")))
  • deep_items: 返回一个类似于标准gdb.Type.iteritems方法的Python迭代器,不同之处在于deep_items返回的迭代器将递归遍历匿名结构或联合字段。例如print(list(gdb.types.deep_items(gdb.lookup_type("struct ColorEnum"))))
  • 其他方法参见上面官方文档
type code集合

定义在py-type.c中pyty_codes

  • gdb.TYPE_CODE_PTR: 指针类型
  • gdb.TYPE_CODE_ARRAY: 数组
  • gdb.TYPE_CODE_STRUCT: 结构体
  • gdb.TYPE_CODE_UNION:共用体
  • gdb.TYPE_CODE_ENUM: 枚举
  • gdb.TYPE_CODE_FLAGS: 位标志类型,用于诸如状态寄存器之类的东西
  • gdb.TYPE_CODE_FUNC: 函数
  • gdb.TYPE_CODE_INT:int类型
  • gdb.TYPE_CODE_FLT:float指针
  • gdb.TYPE_CODE_VOID:void
  • gdb.TYPE_CODE_SET:Pascal集合类型
  • gdb.TYPE_CODE_RANGE: 范围类型,即带边界的整数类型
  • gdb.TYPE_CODE_STRING: 字符串类型。注意这仅用于具有语言定义的字符串类型的某些语言; C字符串不是这样表示的
  • gdb.TYPE_CODE_BITSTRING: A string of bits. It is deprecated
  • gdb.TYPE_CODE_ERROR: 未知的或错误的类型
  • gdb.TYPE_CODE_METHOD: 一种方法类型,在c++中可以找到
  • gdb.TYPE_CODE_METHODPTR: 指向成员函数的指针
  • gdb.TYPE_CODE_MEMBERPTR: 指向成员的指针
  • gdb.TYPE_CODE_REF: 引用类型
  • gdb.TYPE_CODE_RVALUE_REF: c++ 11中的右值引用类型
  • gdb.TYPE_CODE_CHAR: 字符类型
  • gdb.TYPE_CODE_BOOL: 布尔类型
  • gdb.TYPE_CODE_COMPLEX: 复杂浮点类型
  • gdb.TYPE_CODE_TYPEDEF: typedef类型
  • gdb.TYPE_CODE_NAMESPACE: C++ namespace
  • gdb.TYPE_CODE_DECFLOAT: 十进制浮点类型
  • gdb.TYPE_CODE_INTERNAL_FUNCTION: gdb内部的一个函数, 用来表示便利函数(convenience functions)的类型
  • gdb.TYPE_CODE_XMETHOD: gdb内部的一个方法。这是用来表示Xmethod的类型(参见编写Xmethod)
  • gdb.TYPE_CODE_FIXED_POINT: fix-point 定点数
  • gdb.TYPE_CODE_NAMESPACE: Fortran namelist

gdb.Field类型包含如下属性

  • bitpos: 此属性不适用于枚举或静态(如c++)字段。该值是以位计算的,从包含类型开始的位置。注意,在动态类型中,字段的位置可能不是恒定的。在这种情况下,该值将为None。此外,动态类型可能具有在相应的具体类型中没有出现的字段。
  • enumval: 此属性仅对枚举字段可用,其值是枚举成员的整数表示形式。
  • name: 字段名,匿名字段为None。
  • artificial: 如果字段是人为的,则为True,这通常意味着它是由编译器而不是用户提供的。该属性总是被提供,如果字段不是人工的,则为False。
  • is_base_class: 如果字段表示c++结构的基类,则为True。该属性总是提供的,如果字段不是字段参数类型的基类,或者该类型不是c++类,则该属性为False。
  • bitsize: 如果字段是打包的,或者是位字段,那么将有一个非零值,即字段的大小(以位为单位)。否则,这个等于零;在这种情况下,字段的大小由其类型给出。
  • type: 字段的类型。这通常是Type的一个实例,但在某些情况下它可以是None。
  • parent_type: 包含此字段的类型。这是gdb.Type的一个实例。