VS2013 X64环境编译Wingraphviz时返回指针值被截断问题
我最近在尝试为X64架构编译老旧无人维护的Wingraphviz项目时,遇到了一个诡异的崩溃问题,跟你分享下排查和解决的过程:
问题场景
原代码里把getDefaultFont()的调用嵌入到函数参数里,为了调试我把它拆成了单独语句:
const char* def = getDefaultFont(); Deffontname = late_nnstring(g->proto->n,N_fontname,def);
getDefaultFont()逻辑很简单,根据字符集返回字符串字面量的宏定义,比如默认返回DEFAULT_FONTNAME(定义为"Times New Roman")。为了调试我甚至简化了它的返回逻辑:
const char * getDefaultFont() { const char* r = DEFAULT_FONTNAME; return r; }
调试时发现,getDefaultFont()返回时r的地址是正确的,但回到调用函数后def却指向了无效内存,最终late_nnstring()解引用这个损坏的指针导致崩溃。
汇编层面的问题分析
查看对应汇编代码时发现了关键问题:
000007FEDA1244FE call getDefaultFont (07FEDA1291A0h) 000007FEDA124503 cdqe 000007FEDA124505 mov qword ptr [def],rax
调用getDefaultFont()后,RAX寄存器里是正确的.data段指针000007FEDA0C9A20,但紧跟的cdqe指令把RAX的高4字节改成了FFFFFFFF,导致RAX变成FFFFFFFFDA0C9A20,存储到def后就成了无效指针。
问题根源
cdqe指令的作用是把32位的EAX符号扩展为64位的RAX,这说明编译器错误地认为getDefaultFont()的返回值是32位类型,但实际上我们定义的返回值是const char*——在X64架构下这是64位指针类型。
这种情况大概率是因为函数声明和定义不匹配:可能在调用getDefaultFont()的代码前,没有正确包含声明该函数的头文件,或者头文件里的函数声明有误(比如写成了int getDefaultFont()或者32位指针类型),导致编译器误判了返回值的宽度,生成了错误的cdqe指令。
解决方法
- 检查函数声明:确保在调用
getDefaultFont()的源文件中,已经包含了正确声明该函数的头文件,头文件里的声明必须是const char* getDefaultFont(); - 修复类型不匹配:如果头文件里的声明有误,把返回类型修正为
const char* - 清理编译缓存:因为是老旧项目,可能存在编译缓存导致的类型不匹配问题,清理后重新编译
按照这个思路排查后,我解决了这个崩溃问题,希望对你也有帮助!
内容的提问来源于stack exchange,提问作者Bastien Durel

