用汇编调试的方法窥探库函数的实现

本文介绍了如何通过gdb动态调试来深入理解Intel MKL库函数mkl_scsrmv的汇编实现,特别是在面临多分支、优化指令集和函数指针调用等问题时,如何定位到核心运算代码的过程。
摘要由CSDN通过智能技术生成

这篇笔记的起因是我想要偷窥一下IntelMKL库中spmv的实现细节,有可能的话看是不是可以在汇编层面做一些优化的工作。需要分析的MKL库函数是:

void mkl_scsrmv (char *transa, MKL_INT *m, MKL_INT *k, float *alpha, char *matdescra, float *val, MKL_INT *indx,MKL_INT *pntrb, MKL_INT *pntre, float *x, float *beta, float *y);

最初的尝试是简单的从MKL静态库中提取相关的obj文件,通过objdump反汇编得到spmv核心的实现代码。与spmv相关的MKL静态库包括:

libmkl_core.a

最内层的核心运算函数

libmkl_intel_thread.a

中间层分支接口

libmkl_intel_lp64.a

最外层运算接口

 

反汇编的大概方法:

(1)    使用 nm –s 命令导出库文件符号表

(2)    在符号表中搜索顶层函数名称(前面标记T),找出其调用的下层函数(前面标记U)

(3)    如果没有到达最内层的核心运算代码,重复进行第(2)步;否则在符号表中搜索下层函数名称,确定其所在的obj文件

(4)    使用ar –x [file] 命令从库文件中解压出obj文件

(5)    使用 objdump –d 命令反汇编obj文件,得到汇编代码

 

实际操作中发现这种方法想要dump出期望的核心运算函数的汇编代码非常困难,原因有两个:

1.      根据调用的数据格式和参数不同,以及具体硬件平台支持的如SSE等优化指令集的区别,最外层运算函数对应的核心运算函数的分支数量非常大,分支的命名不易弄懂,不易确定正确的调用路径;

2.      在寻找调用路径的过程中,如果仅从符号表不能确定,则需要反汇编出中间层调用部分的代码做参考,但是有些函数调用是用函数指针来实现的,从汇编代码中看不到被调用的函数符号,使得这个过程变得困难。

 

最终我采用的是使用gdb动态调试的方法,来确定最终实际被调用的核心运算函数。具体步骤如下:

1.        编写一个调用mkl_scsrmv的“壳程序”,并静态编译链接MKL库(使用-static编译)

2.        gdb启动程序,设置“display/i $pc”(每次中断打印下一条汇编指令)

3.        b main,run

4.        disassemble显示汇编指令

5.        在欲考察的函数处设置断点,格式是 b *指令地址,如b *( main+offset ) 或 b *0xNNNN

6.        c 执行到断点

7.        si进入函数(对应源代码调试的s);ni运行下一条汇编指令(对应源代码调试的n)

8.        重复4-7,直到进入最内层的核心代码,这时可以直接拷贝disassemble的结果,也可以使用函数分支的符号,到开始导出的符号表中查找对应的obj文件,之后dump出汇编代码

 

确定断点的位置有一些技巧,对于函数分支的命名规律的了解能节省大量的时间;另外需要关注的指令基本只有callq一种,但是要注意callq接着的不一定是函数符号,比如使用函数指针的情况下,callq后面是寄存器名,此时要确定调用的函数分支就完全依靠gdb的动态调试了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值