extern 一个函数指针 引发的问题
本帖最后由 mrpre 于 2015-04-30 20:01 编辑首先是这样的,我在一个文件里面 定义了一个函数指针:
s32 (*test_func_ptr)(void * buf, s32 len, void * session) = dummy_func();
我需要在另外一个文件使用该函数,第一次向下面一样使用(死的稀里哗啦的)
extern s32 test_func_ptr(void * buf, s32 len, void * session);
int test_func()
{
void *ptr1, *ptr2;
int a;
test_func_ptr(ptr1, a, ptr2);
return 0;
}
panic了。我看了一下汇编
0xffffffffc0380198 test_func: daddiu sp,sp,-16
0xffffffffc038019c test_func+0x4: lui v0,0xc045
0xffffffffc03801a0 test_func+0x8: move a0,zero
0xffffffffc03801a4 test_func+0xc: move a1,zero
0xffffffffc03801a8 test_func+0x10: sd ra,8(sp)
0xffffffffc03801ac test_func+0x14: daddiu v0,v0,30544
0xffffffffc03801b0 test_func+0x18: jalr v0 //v0 是 test_func_ptr这个符号
0xffffffffc03801b4 test_func+0x1c: move a2,zero
0xffffffffc03801b8 test_func+0x20: ld ra,8(sp)
0xffffffffc03801bc test_func+0x24: move v0,zero
0xffffffffc03801c0 test_func+0x28: jr ra
0xffffffffc03801c4 test_func+0x2c: daddiu sp,sp,16
kdb> 0xffffffffc0450000+30544
0xffffffffc0450000 = 0xffffffffc0457750 ( test_func_ptr)
然后我把 extern s32 test_func_ptr(void * buf, s32 len, void * session);
变换为 extern s32 (*test_func_ptr)(void * buf, s32 len, void * session);
这下总对了吧,果然,不再panic
但是问题来了,我查看了一下 test_func 对应的汇编代码。。。居然和上面的没区别(基本没区别)。
kdb> id test_func
0xffffffffc037d040 test_func: lui v0,0xc045
0xffffffffc037d044 test_func+0x4: daddiu sp,sp,-16
0xffffffffc037d048 test_func+0x8: move a0,zero
0xffffffffc037d04c test_func+0xc: move a1,zero
0xffffffffc037d050 test_func+0x10: ld v0,30544(v0)
0xffffffffc037d054 test_func+0x14: sd ra,8(sp)
0xffffffffc037d058 test_func+0x18: jalr v0 //v0 还是 test_func_ptr这个符号,我觉得应该jalr dummy_func
0xffffffffc037d05c test_func+0x1c: move a2,zero
0xffffffffc037d060 test_func+0x20: ld ra,8(sp)
0xffffffffc037d064 test_func+0x24: move v0,zero
0xffffffffc037d068 test_func+0x28: jr ra
0xffffffffc037d06c test_func+0x2c: daddiu sp,sp,16
我对底层的东西不太平白,觉得如果和“编译”没关系的话,是不是和“链接”有关系?
望有大神释疑。 本帖最后由 linuxfellow 于 2015-04-30 10:14 编辑
C语言的问题,与编译器和底层无关。
这个区别很大吧。
x86下是两种不同的call:
[~]$ cat a.c
externvoid f1(void);
externvoid (*f2)(void);
void f(void)
{
f1();
f2();
}
[~]$ objdump -d a.o
a.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <f>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: e8 00 00 00 00 callq9 <f+0x9> #直接寻址
9: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 10 <f+0x10>
10: ff d0 callq*%rax #间接寻址
12: c9 leaveq
13: c3 retq 本帖最后由 karma303 于 2015-04-30 12:59 编辑
是编译阶段的问题。
你已经在另一个模块里,把test_func_ptr定义成函数指针。那么,“test_func_ptr”就是个普通的symbol,这个symbol本身的值不重要(假定是804a010),在804a010内存处存放的只是一个4byte的数值,value是test_func这个函数的地址。
再回到当前的模块。
你欺骗编译器说test_func_ptr只是个函数,编译器认为,804a010内存地址起,存放着一段函数代码(instruction)。编译器当然直接跳过去:push eip; jmp (804a010-当前eip)----------也就是call test_func_ptr.
如果编译器知道test_func_ptr只是一小块儿函数指针,它会jmp dword ,就是从test_func_ptr这儿取出真正的目标函数地址,来调整eip。写成mov eax,;jmp eax大同小异。
-----------------------
借一下3楼的代码,用intel汇编看一下:
<a.c>
void f2(void){ }
void (*f1)(void) = f2;
<t.c>
extern void (*f1)(void);
extern void f2(void);
void main(void){
f1();
f2();
}
gcc -o t t.c a.c
objdump -d -Mintel ./t
080483b4 <main>:
80483b4: 55 push ebp
80483b5: 89 e5 mov ebp,esp
80483b7: 83 e4 f0 and esp,0xfffffff0
80483ba: a1 10 a0 04 08 mov eax,ds:0x804a010
80483bf: ff d0 call eax
80483c1: e8 02 00 00 00 call 80483c8 <f2>
80483c6: c9 leave
80483c7: c3 ret
------------PS:上面有些“jmp”该写成“call”,我是觉得jmp清晰些。
看大家的回复,意思说 理论上确实两段汇编代码不一样......我也理解大家说的指针和变量之间的关系,间接寻址和直接寻址之间的关系(一开始我没看汇编,也这么理解,后来看了汇编我就郁闷了)。可是这的的确确发生了,两个相似的汇编,一个跑起来了一个panic了……难道是mips的问题?
页:
[1]