本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
本作品 (李兆龙 博文, 由 李兆龙 创作),由 李兆龙 确认,转载请注明版权。
引言
其实这还是一篇记录bug修复的文章,以前写这类文章的时候喜欢起一个“问题解决:XXXX”的题目,清晰有余,故事性则欠缺不少,自己在日后看到时多多少少忘了写那篇文章时的场景,既然二百多篇文章都过来了,现在为什么又突然开始矫情起来呢?究其原因,还是以前在学校时时间太过幸福了,有那么多时间去做这样的输出,所以写文章初始的那份快乐早就从夏天的第一口可乐,清晨路上偶遇的清纯妹子这种显而易见的愉悦变成了麻木的敲键盘了。
直到现在每天累死累活的时候才知道这种闲暇的输出有多么有趣,多么值得被回忆,所以对抗狗日的生活的第一步就从没有技术标题的技术文章开始。
本篇文章内容均做脱敏处理。
问题描述
这篇文章描述的问题是这样一个场景,我有一个诸如void* test()
的函数声明,在另一处文件被调用后所有从这个函数返回的指针均被截取一半。
上面那个0x7ffff644d7f0
是我在函数中希望穿出的指针实际存储的地址,到了外面以后可以看到变成了0xfffffffff644d7f0
。
全局这个函数只有一个地方被使用,传出的那个oldIntance变量(拼错了)是一个局部地址,存储的指针是一个全局变量,所以立马怀疑的就是可能oldIntance上下有变量越界了。但是仔细检查以后发现并没有(其实注释了再gdb一下也可)。
这种问题到可能是野指针造成的,但是这个问题是可以稳定复现的,所以野指针的概率也不大。
OK,放大招
可以从0x0000000000587de3看到oldIntance被存储在栈上。
最后 0x0000000000587e9e返回的时候把栈上的值放到rax
0x49916f处函数返回以后从rax拿值。
打印汇编看了以后也没什么问题。
究竟是什么原因呢,好在此时有大神来协助我解决问题,先make clean
,再make
,一下子出现了一个我之前忽略的warning,即:
warning: implicit declaration of function ‘ReturnlatencyInstanceAndSwap’ [-Wimplicit-function-declaration]
warning: initialization makes pointer from integer without a cast
void* oldInstance = lizhaolongnihao();
函数的隐式声明?一个指针到整数到转换?
借用其他博主的一句话:在c语言中函数声明不是必须的,即使没有声明函数,gcc编译器也只是会提示警告。但是函数声明却是很有必要的。
编译器在编译过程中依次生成与源文件对应的可重定位目标文件(.o),每个源文件中调用的函数在链接前都是以符号的形式体现在.o文件中。在编译过程中不会去检查某个函数的形式,因为函数参数是通过寄存器和压栈来处理的,直接把函数翻译成符号,编译器是不知道关于函数参数的信息的,最后交给连接器把符号翻译成地址。所以链接的时候只要能找到对应得符号就不会报错。
所以问题的原因是没有指定头文件,编译不会出现问题,因为符号存在于.o文件中,但是编译器把返回类型改为了int,所以导致传出的指针有四个字节被截断。
总结
早点注意warning就不会有那么多问题,这么一个小坑足足填了两个多小时。这也是这篇文章题目的由来。
参考: