首页> 中国专利> 一种实现用户态调试器调试单个函数的方法及系统

一种实现用户态调试器调试单个函数的方法及系统

摘要

本发明公开了一种实现用户态调试器调试单个函数的方法及系统,系统包括符号表操作模块、寄存器操作模块、内存操作模块、运行控制模块、运行环境构建模块;方法为:A.符号表操作模块分析当前的被调试程序及其动态库的符号信息,建立哈希表;运行控制模块从被调试程序的任务中选择一个作为待处理任务;B.运行控制模块从哈希表查找线程创建库函数,找到后停止运行待处理任务;C.运行环境构建模块依据线程创建库函数及待调试函数,调用寄存器和内存操作模块来修改待处理任务的各寄存器和堆栈中的值,以改变待处理任务的行为,使其在之后运行时创建线程来执行待调试函数,之后运行待处理任务。采用本发明,实现了在各种CPU上通过创建线程来运行单个函数的功能。

著录项

  • 公开/公告号CN101446918A

    专利类型发明专利

  • 公开/公告日2009-06-03

    原文格式PDF

  • 申请/专利权人 中兴通讯股份有限公司;

    申请/专利号CN200810218397.6

  • 发明设计人 向红;

    申请日2008-12-10

  • 分类号G06F11/36(20060101);

  • 代理机构深圳市永杰专利商标事务所;

  • 代理人曹建军

  • 地址 518057 广东省深圳市南山区高新技术产业园科技南路中兴通讯大厦法律部

  • 入库时间 2023-12-17 22:01:59

法律信息

  • 法律状态公告日

    法律状态信息

    法律状态

  • 2018-12-07

    未缴年费专利权终止 IPC(主分类):G06F11/36 授权公告日:20111228 终止日期:20171210 申请日:20081210

    专利权的终止

  • 2011-12-28

    授权

    授权

  • 2010-05-19

    实质审查的生效 IPC(主分类):G06F11/36 申请日:20081210

    实质审查的生效

  • 2009-06-03

    公开

    公开

说明书

技术领域

本发明涉及调试器的实现技术,尤其涉及一种实现用户态调试器调试单个函数的方法及系统。

背景技术

调试器是用来帮助开发人员定位程序故障的一种工具软件。目前的调试器总体分为两类:内核级调试器和用户态调试器。内核级调试器能够跟踪和调试运行在内核态的任务;用户态调试器只能对运行在用户态的任务进行调试。调试器提供了设置断点,查看变量、寄存器、显示堆栈和求表达式值等基本调试功能,以及控制程序运行的单步跳过,单步进入,继续运行等功能。这些功能对于程序员跟踪和发现故障起着重要作用。

在程序设计语言中,函数作为逻辑上相对独立的代码片断,能够完成函数设计者所指定的功能。函数功能的正确性对程序整体行为的正确性起着决定作用,因此,如何针对单个函数进行调试并验证其正确性,成为调试的一种需要。

以WindRiver公司的Vxworks(一种嵌入式实时操作系统)为例,它提供的shell调试器,就具备调试单个函数的功能。通过在shell中执行命令,创建新的任务,该任务执行命令指定的函数,并能够对其进行调试。Vxworks的shell调试器属于内核级调试器,被运行起来的函数是在内核态运行的。其功能的实现依赖于内核提供的创建任务的接口函数,通过传入要运行的函数的地址以及函数参数来完成。由此可知,在内核态下,只需要调用内核任务创建函数,并传入要创建函数的入口地址,即可实现调试单个函数的功能。

然而,对于用户态调试器,在用户态下没有一个统一的接口实现在其它进程中创建新线程。具体的说,用户态的程序通常被称为一个进程,一个进程可以拥有多个线程,线程之间共享同一个进程的资源;而进程之间的地址空间却是相互独立的,每个进程都有自己的虚拟地址空间,一个进程不能直接读写其它进程的虚拟地址空间的内容;同时,操作系统没有提供在其它进程中创建线程运行单个函数的接口。因此,用户态调试器作为用户态下运行的一个普通进程,要实现在被调试任务中创建线程运行单个函数,无法直接利用操作系统的接口完成。在目前的用户态调试器中还没有创建线程运行单个函数功能的实现。

发明内容

本发明所要解决的技术问题是提供一种实现用户态调试器调试单个函数的方法,以及实现该方法的系统。

为解决上述技术问题,本发明是通过以下技术方案实现的:

一种实现用户态调试器调试单个函数的方法,所述用户态调试器在收到调试所述单个函数的命令后,将该函数作为当前的待调试函数,并作以下处理:

A、对当前的被调试程序以及被调试程序加载的动态库的符号信息进行分析,根据分析结果建立哈希表;同时,从所述被调试程序的任务中选择一个任务作为待处理任务;

B、在所述哈希表中查找线程创建库函数的入口地址,当找到后,停止所述待处理任务的运行;

C、以所述线程创建库函数及所述待调试函数为依据,修改所述待处理任务的各寄存器和堆栈中的值,以改变所述待处理任务的行为,使其在之后运行时创建线程来执行所述待调试函数,修改成功后运行所述待处理任务。

其中,所述步骤B中,在停止所述待处理任务的运行之后,还包括:将当前所述待处理任务的全部寄存器和堆栈中的值进行保存;

步骤C之后还包括:根据所述待处理任务返回的信号确认所述待调试函数的运行是否结束,若已结束,则将所述待处理任务的全部寄存器和堆栈中的值恢复为所述步骤B中在停止所述待处理任务的运行之后所保存的值。

其中,所述步骤B中还包括:若在所述哈希表中未查找到线程创建库函数的入口地址,则将线程库加载到所述被调试程序中,加载成功后,将所述线程库的符号信息添加入所述哈希表中,再从该哈希表中查找出所述线程创建库函数的入口地址。

其中,步骤B中,所述线程库加载到所述被调试程序中的方法为:

①在标准C函数库中查找动态库加载函数的入口地址;

②将所述待处理任务的程序计数器的值设置为动态库加载函数的入口地址;

③从所述待处理任务的堆栈指针寄存器中读取当前栈顶的地址;以此地址为起始地址,在所述待处理任务的堆栈上构建所述动态库加载函数的调用栈帧,该调用栈帧中包括动态库路径、模式、动态库路径地址及返回地址参数;其中,所述动态库路径为线程库的路径,所述模式为所述线程库的加载模式,所述动态库路径地址为所述线程库的路径地址;

④设置待处理任务的堆栈指针寄存器的值为新的栈顶地址。

其中,所述步骤C中,所述待处理任务的各寄存器和堆栈中的值的修改方法为:

e1、设置所述待处理任务的程序计数器的值为线程创建库函数的入口地址;

e2、从所述待处理任务的堆栈指针寄存器中读取当前栈顶的地址;

e3、以所述当前栈顶的地址为起始地址,在所述待处理任务的堆栈中,构建所述待调试函数的函数调用栈帧;

e4、设置所述堆栈指针寄存器中的指针指向新的栈顶地址。

其中,所述步骤e3中,所述待调试函数的函数调用栈帧的构建过程包括:

m1、在所述堆栈上分配存放线程内部标识的局部变量的空间,写入所述线程内部标识的局部变量;

m2、在堆栈上分配存放待调试函数的参数的空间,写入所述待调试函数的各项参数;

m3、在所述堆栈上分配空间,依次写入待调试函数的入口地址、线程属性、线程内部标识局部变量的地址。

一种实现用户态调试器调试单个函数的系统,包括符号表操作模块、寄存器操作模块、内存操作模块,还包括:运行控制模块、运行环境构建模块;

所述符号表操作模块,用于对被调试程序以及被调试程序所加载的动态库的符号信息进行分析,根据分析结果建立哈希表;

所述寄存器操作模块,用于对被调试程序的任务的各寄存器的值进行读写操作;

所述内存操作模块,用于对被调试程序的任务的堆栈中的值进行读取和修改的操作;

所述运行控制模块,用于接收调试单个函数的命令,将所述单个函数作为待调试函数,进行以下操作:从当前被调试程序的任务中选取一个任务作为待处理任务,调用符号表操作模块建立哈希表并从中查找线程创建库函数的入口地址,然后停止所述待处理任务的运行;控制运行环境构建模块以所述线程创建库函数及待调试函数为依据来改变所述待处理任务的行为,使该任务在之后运行时创建线程来执行所述待调试函数,并在修改完成之后启动所述待处理任务的运行;

所述运行环境构建模块,用于以所述线程创建库函数及待调试函数为依据,通过调用寄存器操作模块和内存操作模块修改所述待处理任务的各寄存器和堆栈的值来改变所述待处理任务的行为,为所述待调试函数创建运行环境。

上述系统中,还包括动态库加载模块,用于向被调试程序中加载指定的动态库;

所述运行控制模块,还用于在从所述哈希表中未查找到线程创建库函数的入口地址时,通过调用动态库加载模块向所述被调试程序中加载线程库,并调用符号表操作模块将所述线程库的符号信息添加入所述哈希表中,然后从该哈希表中重新查找所述线程创建库函数。

上述系统中,还包括信号处理模块和运行环境恢复模块;

所述信号处理模块,用于等待接收所述待处理任务在运行过程中返回的信号,根据信号的类型判断所述待调试函数的运行是否结束,若已结束,则通知运行环境恢复模块;

所述运行环境恢复模块,用于调用寄存器操作模块,保存所述待处理任务的各寄存器和堆栈在被所述运行环境构建模块修改之前的值,并在收到信号处理模块的通知后,将所述待处理任务的各寄存器和堆栈的值恢复为之前保存的值。

本发明具有以下有益效果:

采用本发明,能够在各种CPU上实现创建线程运行单个函数的功能,克服了目前的用户态调试器中缺少对运行单个函数进行调试的支持。

附图说明

图1是本发明的系统结构示意图;

图2是寄存器操作模块的结构示意图;

图3是内存操作模块的结构示意图;

图4是符号表操作模块的结构示意图;

图5是信号处理模块的结构示意图;

图6是本发明的用户态调试器调试单个函数的方法流程图;

图7是本发明中动态加载库到被调试程序中的方法流程图;

图8是本发明动态加载线程库过程中的堆栈布局示意图;

图9是本发明创建线程运行单个函数过程中的堆栈布局示意图;

图10是本发明中信号处理循环流程的示意图。

具体实施方式

本发明的核心思想为:通过调试器来修改被调试程序的执行环境,改变被调试任务的行为,使其创建线程来执行用户指定的待调试函数,待待调试函数运行完成后,再恢复被调试程序原来的执行环境。

下面结合附图及具体实施例对本发明作进一步详细的描述:

请参阅图1,本发明所述的实现用户态调试器运行单个函数的系统包括以下八个模块:寄存器操作模块、内存操作模块、符号表操作模块、信号处理模块、动态库加载模块、运行环境构建模块、运行环境恢复模块、运行控制模块。其中寄存器操作模块,内存操作模块、符号表操作模块为调试器通用功能模块,其它为本发明所涉及的创建线程运行单个函数功能的模块。

寄存器操作模块,用于对被调试程序的寄存器的值进行读写操作。如图2所示,包括寄存器映射缓存、读、写寄存器接口以及寄存器操作接口。其中寄存器映射缓存用于保存当前任务的所有寄存器的值,通过读寄存器接口将当前任务的所有寄存器的值读取到寄存器映射缓存中;通过写寄存器接口,将寄存器映射缓存的内容写入到任务的寄存器中。寄存器操作接口对外提供了读写寄存器的统一接口。

内存操作模块,用于对被调试程序的虚拟地址空间进行读取和修改的操作。如图3所示,通过系统接口,实现将内存缓冲区的内容写到指定的地址或者将指定的地址的内容读出到内存缓冲区中。

符号表操作模块,如图4所示,用于分析被调试程序的符号信息以及被调试程序加载的动态库的符号信息,建立hash表(哈希表),提供通过符号名查找符号地址和通过符号地址查找符号名的功能。

信号处理模块,用于根据被调试程序向调试器发送的信号,感知新线程创建等异步事件。如图5所示,调试器首先注册需要监听的信号集合,然后监听异步信号事件。对于收到的异步信号,分析该信号的类型,并处理该信号,或者通知用户。

动态库加载模块,用于控制被调试程序加载指定的动态库。该模块的处理流程如图7所示。

运行环境构建模块,用于为运行单个函数做好运行前的环境构建工作。

运行环境恢复模块,用于恢复被调试程序在创建线程运行单个函数前的所有状态。该模块主要通过调用寄存器操作模块,恢复所有寄存器和堆栈的内容。

运行控制模块,用于对创建线程运行单个函数流程的总体控制和错误处理等。

上述系统中的工作原理为:

运行控制模块在接收到调试单个函数的命令后,将所述单个函数作为待调试函数,进行以下操作:从当前被调试程序的任务中选取一个任务作为待处理任务;调用符号表操作模块建立哈希表并从中查找线程创建库函数的入口地址,若未查找到,则调用动态库加载模块,向所述被调试程序中加载线程库,并调用符号表操作模块将所述线程库的符号信息添加入所述哈希表中,然后从该哈希表中重新查找所述线程创建库函数;找到后,停止待处理任务的运行,控制运行环境构建模块以所述线程创建库函数及待调试函数为依据来改变待处理任务的行为,使该任务在之后运行时创建线程来执行所述待调试函数,并在修改完成之后启动所述待处理任务的运行。在运行过程中,信号处理模块等待接收待处理任务在运行过程中返回的信号,根据信号的类型判断所述待调试函数的运行是否结束,若已结束,则通知运行环境恢复模块,将待处理任务的各寄存器和堆栈的值恢复为之前的初始状态值。

请参阅图6,该图所示为本发明的用户态调试器调试单个函数的方法,具体包括以下步骤:

601、对被调试程序以及被调试程序加载的动态库的符号信息进行分析,根据分析结果建立哈希表:通过读取和分析被调试程序的可执行文件,获取被调试程序符号的名称、虚拟地址和类型信息(符号类型包括全局函数,全局变量,静态函数,静态变量等),把所有符号信息以符号名为关键字,建立hash表;同理,对于该被调试程序加载的所有动态库,也按照上述方法将其符号信息加入到hash表中。同时,从被调试程序的多个任务之中选择一个任务作为待处理任务。

602、在hash表中查找pthread_create函数(线程创建库函数)的入口地址。该地址是线程库中的符号地址,如果在hash表中没有查找到,则执行步骤603,否则执行步骤604。

603、调用动态库加载模块,将libpthread.so.0线程库加载到被调试程序中;加载成功后,再次调用符号表操作模块,将libpthread.so.0线程库的符号信息添加到hash表中,然后从该表中查找出pthread_create的入口地址。

请参阅图7,上述线程库的加载方法为:

701、在libc库(标准C函数库)中查找dl_open函数(动态库加载函数)的入口地址。

702、将待处理任务的pc寄存器(程序计数器)的值设置为dl_open的入口地址。

703、在待处理任务的堆栈上构造dl_open函数的调用栈帧,新构造的堆栈的布局如图8所示,该布局适用于x86体系结构,对其它架构CPU可参照其ABI接口中的定义。在新构造的栈帧中,首先是一个存放线程库全路径的缓冲区;其次是参数mode,表明打开线程库的模式,设置为RTLD_NOW,表示立即加载;然后是线程库路径的地址,即指向最开始分配的存放库路径的缓冲区地址(dl_open函数的第一个参数是库路径的地址,第二个参数是mode,本实施例中是将这两个参数凡方向顺序写入堆栈的);最后是返回地址,本实施例中将此返回地址设置为一个异常地址,用于信号处理模块判断函数调用的结束。

704、设置待处理任务的sp寄存器(堆栈指针寄存器)的指针指向新的栈顶。

705、在构造好dl_open函数的调用栈后,继续待处理任务的运行,待处理任务就会从dl_open函数的入口地址处开始执行,并加载线程库。

706、从eax寄存器中读取dl_open函数的返回值,如果加载成功,则返回值为线程库的一个句柄;如果加载失败,则返回值为-1。

604、停止待处理任务的运行;并调用运行环境恢复模块,将待处理任务的所有寄存器和堆栈中的当前值保存起来。

605、调用运行环境构建模块,构建待调试函数的运行环境:

i)设置待处理任务的pc寄存器的值为pthread_create的入口地址。

ii)根据不同CPU的ABI(应用程序二进制接口)中对函数调用的定义不同,构建待调试函数的函数调用栈帧,将待调试函数的各项参数放置到堆栈中的正确位置,并将函数调用后的返回地址设置为一个异常地址;构建方法为先在所述堆栈上分配存放线程内部标识的局部变量的空间,写入所述线程内部标识的局部变量;再在堆栈上分配存放待调试函数的参数的空间,写入所述待调试函数的各项参数;最后在所述堆栈上分配空间,依次写入待调试函数的入口地址、线程属性、线程内部标识局部变量的地址。新构建的堆栈布局如图9所示。

iii)设置待处理任务的sp寄存器中的指针指向新的栈顶。

606、运行待处理任务。

607、信号处理模块等待接收待处理任务发送给调试器的异步信号,并根据所接收的异步信号的类型作不同的处理,如图10所示。如果收到新线程创建的信号(在linux2.6内核下,可以通过对SIGTRAP信号进行移位操作,并取得其reason字段进行判断),将新创建的线程加入到被调试的任务链表中管理起来,然后继续等待待处理任务的信号;如果收到异常信号,则说明该任务调用pthread_create函数创建线程结束,执行步骤608;如果收到其他信号,则处理该信号后继续等待待处理任务的信号。

608、调用运行环境恢复模块,恢复之前保存的所有寄存器和堆栈的值,回到待处理任务的最初状态。

以上实施例仅用以说明本发明的技术方案而非限制,仅仅参照较佳实施例对本发明进行了详细说明。本领域的普通技术人员应当理解,可以对本发明的技术方案进行修改或者等同替换,而不脱离本发明技术方案的精神和范围,均应涵盖在本发明的权利要求范围当中。

去获取专利,查看全文>

相似文献

  • 专利
  • 中文文献
  • 外文文献
获取专利

客服邮箱:kefu@zhangqiaokeyan.com

京公网安备:11010802029741号 ICP备案号:京ICP备15016152号-6 六维联合信息科技 (北京) 有限公司©版权所有
  • 客服微信

  • 服务号