C语言回调函数在CPU上的指令运行分析

1. 什么是回调函数?

其实回调函数就是一种利用函数指针进行函数调用的过程。

一般使用场景是用户使用底层API时,需要在API运行时再使用自己的函数,这时就会用到回调函数,即将自己的函数作为函数指针参数调用底层API,API运行中根据函数指针回调用户自己的函数。

比如:用户利用一个加密机制的API进行加密,但是用户想自定义加密算法,这时就可以用回调函数,把自定义加密算法封装成函数,函数指针作为参数传入API,用户调用API,API运行时再根据指针回调用户的回调函数。

2.以上是引子,很多文章都对回调函数有更清晰深刻的解读,有兴趣的可以搜索查看,本文重在进行CPU上汇编指令分析,下面以一个简单的回调函数demo为例进行运行指令的分析。

3.C语言源码如下:

 14 bool test_func(int a, int b);
 15 void test_callback(int c, bool (t_func)(int a, int b), int d);
 16 
 17 int main(void)
 18 {
 19         int aa = 1;
 20         int bb = 2;
 21         test_callback(aa, test_func, bb);
...
212 bool test_func(int a, int b){
213     return true;
214 }
215 
216 void test_callback(int c, bool (t_func)(int a, int b), int d){
217     t_func(c,d);
218 }

4. 到bochs虚拟机上单步调试运行,结果如下:

(0) [0x00000000a02c] 0010:c000a02c (unk. ctxt): mov dword ptr ss:[ebp-76], 0x00000001 ; c745b401000000	
<bochs:17> n
Next at t=7979555
(0) [0x00000000a033] 0010:c000a033 (unk. ctxt): mov dword ptr ss:[ebp-72], 0x00000002 ; c745b802000000	
<bochs:18> n
Next at t=7979556
(0) [0x00000000a03a] 0010:c000a03a (unk. ctxt): sub esp, 0x00000004       ; 83ec04		
<bochs:19> n
Next at t=7979557
(0) [0x00000000a03d] 0010:c000a03d (unk. ctxt): push dword ptr ss:[ebp-72] ; ff75b8		
<bochs:20> n
Next at t=7979558
(0) [0x00000000a040] 0010:c000a040 (unk. ctxt): lea eax, ds:[ebx-28057]   ; 8d836792ffff	
<bochs:21> r
eax: 0x00000000 0
ebx: 0xc0011000 -1073672192
ecx: 0xc0007004 -1073713148
edx: 0x00000020 32
esp: 0xc0006f98 -1073713256
ebp: 0xc0006ff8 -1073713160
esi: 0xc0100000 -1072693248
edi: 0x00000000 0
eip: 0xc000a040
eflags 0x00000096: id vip vif ac vm rf nt IOPL=0 of df if tf SF zf AF PF cf
<bochs:22> n
Next at t=7979559
(0) [0x00000000a046] 0010:c000a046 (unk. ctxt): push eax                  ; 50		
<bochs:23> r
eax: 0xc000a267 -1073700249
ebx: 0xc0011000 -1073672192
ecx: 0xc0007004 -1073713148
edx: 0x00000020 32
esp: 0xc0006f98 -1073713256
ebp: 0xc0006ff8 -1073713160
esi: 0xc0100000 -1072693248
edi: 0x00000000 0
eip: 0xc000a046
eflags 0x00000096: id vip vif ac vm rf nt IOPL=0 of df if tf SF zf AF PF cf
<bochs:24> x 0xa267
[bochs]:
0x0000a267 <bogus+       0>:	0xfb1e0ff3
<bochs:25> n
Next at t=7979560
(0) [0x00000000a047] 0010:c000a047 (unk. ctxt): push dword ptr ss:[ebp-76] ; ff75b4		
<bochs:26> n
Next at t=7979561
(0) [0x00000000a04a] 0010:c000a04a (unk. ctxt): call .+560 (0xc000a27f)   ; e830020000	
<bochs:27> s
Next at t=7979562
(0) [0x00000000a27f] 0010:c000a27f (unk. ctxt): nop                       ; f30f1efb
<bochs:28> n
Next at t=7979563
(0) [0x00000000a283] 0010:c000a283 (unk. ctxt): push ebp                  ; 55
<bochs:29> n
Next at t=7979564
(0) [0x00000000a284] 0010:c000a284 (unk. ctxt): mov ebp, esp              ; 89e5
<bochs:30> n
Next at t=7979565
(0) [0x00000000a286] 0010:c000a286 (unk. ctxt): sub esp, 0x00000008       ; 83ec08
<bochs:31> n
Next at t=7979566
(0) [0x00000000a289] 0010:c000a289 (unk. ctxt): call .+25 (0xc000a2a7)    ; e819000000
<bochs:32> n
Next at t=7979569
(0) [0x00000000a28e] 0010:c000a28e (unk. ctxt): add eax, 0x00006d72       ; 05726d0000
<bochs:33> n
Next at t=7979570
(0) [0x00000000a293] 0010:c000a293 (unk. ctxt): sub esp, 0x00000008       ; 83ec08	
<bochs:34> n
Next at t=7979571
(0) [0x00000000a296] 0010:c000a296 (unk. ctxt): push dword ptr ss:[ebp+16] ; ff7510	
<bochs:35> n
Next at t=7979572
(0) [0x00000000a299] 0010:c000a299 (unk. ctxt): push dword ptr ss:[ebp+8] ; ff7508
<bochs:36> n
Next at t=7979573
(0) [0x00000000a29c] 0010:c000a29c (unk. ctxt): mov eax, dword ptr ss:[ebp+12] ; 8b450c
<bochs:37> n
Next at t=7979574
(0) [0x00000000a29f] 0010:c000a29f (unk. ctxt): call eax                  ; ffd0
<bochs:38> r
eax: 0xc000a267 -1073700249
ebx: 0xc0011000 -1073672192
ecx: 0xc0007004 -1073713148
edx: 0x00000020 32
esp: 0xc0006f70 -1073713296
ebp: 0xc0006f88 -1073713272
esi: 0xc0100000 -1072693248
edi: 0x00000000 0
eip: 0xc000a29f
eflags 0x00000096: id vip vif ac vm rf nt IOPL=0 of df if tf SF zf AF PF cf
<bochs:39> n
Next at t=7979585
(0) [0x00000000a2a1] 0010:c000a2a1 (unk. ctxt): add esp, 0x00000010       ; 83c410
<bochs:40> r
eax: 0x00000001 1
ebx: 0xc0011000 -1073672192
ecx: 0xc0007004 -1073713148
edx: 0x00000020 32
esp: 0xc0006f70 -1073713296
ebp: 0xc0006f88 -1073713272
esi: 0xc0100000 -1072693248
edi: 0x00000000 0
eip: 0xc000a2a1
eflags 0x00000096: id vip vif ac vm rf nt IOPL=0 of df if tf SF zf AF PF cf
<bochs:41> n
Next at t=7979586
(0) [0x00000000a2a4] 0010:c000a2a4 (unk. ctxt): nop                       ; 90
<bochs:42> n
Next at t=7979587
(0) [0x00000000a2a5] 0010:c000a2a5 (unk. ctxt): leave                     ; c9
<bochs:43> n
Next at t=7979588
(0) [0x00000000a2a6] 0010:c000a2a6 (unk. ctxt): ret                       ; c3

5. 逐行分析汇编指令

(0) [0x00000000a02c] 0010:c000a02c (unk. ctxt): mov dword ptr ss:[ebp-76], 0x00000001 ; c745b401000000

对应C语句:int aa =1;

(0) [0x00000000a033] 0010:c000a033 (unk. ctxt): mov dword ptr ss:[ebp-72], 0x00000002 ; c745b802000000

对应C语句: int bb =2;

(0) [0x00000000a03a] 0010:c000a03a (unk. ctxt): sub esp, 0x00000004 ; 83ec04

栈指针减4,预留空间

(0) [0x00000000a03d] 0010:c000a03d (unk. ctxt): push dword ptr ss:[ebp-72] ; ff75b8

为调用test_callback传参数3,即传入bb

(0) [0x00000000a040] 0010:c000a040 (unk. ctxt): lea eax, ds:[ebx-28057] ; 8d836792ffff

将参数2(回调函数)的地址赋给eax

(0) [0x00000000a046] 0010:c000a046 (unk. ctxt): push eax ; 50

传入参数2,对照readelf看到的回调函数地址和寄存器eax值,可以看到eax的值(0xc000a267)确实就是bool test_func(int a, int b)这个函数的地址  r eax: 0xc000a267 -1073700249      450: c000a267 24 FUNC GLOBAL DEFAULT 1 test_func

(0) [0x00000000a047] 0010:c000a047 (unk. ctxt): push dword ptr ss:[ebp-76] ; ff75b4

为调用test_callback传参数1,即传入aa

(0) [0x00000000a04a] 0010:c000a04a (unk. ctxt): call .+560 (0xc000a27f) ; e830020000

调用test_callback, readelf看到: 479: c000a27f 40 FUNC GLOBAL DEFAULT 1 test_callback (0) [0x00000000a27f] 0010:c000a27f (unk. ctxt): nop ; f30f1efb

(0) [0x00000000a283] 0010:c000a283 (unk. ctxt): push ebp ; 55

(0) [0x00000000a284] 0010:c000a284 (unk. ctxt): mov ebp, esp ; 89e5

(0) [0x00000000a286] 0010:c000a286 (unk. ctxt): sub esp, 0x00000008 ; 83ec08

(0) [0x00000000a289] 0010:c000a289 (unk. ctxt): call .+25 (0xc000a2a7) ; e819000000

(0) [0x00000000a28e] 0010:c000a28e (unk. ctxt): add eax, 0x00006d72 ; 05726d0000

(0) [0x00000000a293] 0010:c000a293 (unk. ctxt): sub esp, 0x00000008 ; 83ec08

以上几句是进函数的保护和调用__x86_pc_thunk,与主题无关。                             

以下对应test_callback中的语句t_func(c,d);, t_func是形参,参数c和d也是形参,实际运行时替换为实参,即实际等于执行test_func(aa,bb); ,后面就是简单的函数调用了,不再详述

(0) [0x00000000a296] 0010:c000a296 (unk. ctxt): push dword ptr ss:[ebp+16] ; ff7510

(0) [0x00000000a299] 0010:c000a299 (unk. ctxt): push dword ptr ss:[ebp+8] ; ff7508

(0) [0x00000000a29c] 0010:c000a29c (unk. ctxt): mov eax, dword ptr ss:[ebp+12] ; 8b450c

(0) [0x00000000a29f] 0010:c000a29f (unk. ctxt): call eax ; ffd0

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值