fprintf函数调用出core

最近写了一段代码,程序core在了打日志的地方,很诡异。

        LOG_FATAL("[log_id=%ld]sql exception throw, mysql error code = %d, exception %s",
                request.log_id,
                e.getErrorCode(),
                e.what());

LOG_FATAL这个宏是自己封装的一个打日志的函数,底层会调用log4cpp的接口,再底层当然就是调用fprintf,vfprintf等。
很普通的一个函数,在代码运行到这里时出core了。


#0  0x0049fe29 in vfprintf () from /lib/i386-linux-gnu/libc.so.6
#1  0x004c526b in vsnprintf () from /lib/i386-linux-gnu/libc.so.6
#2  0x081a08be in log4cpp::StringUtil::vform(char const*, char*) ()
#3  0x081942f6 in log4cpp::Category::_logUnconditionally(int, char const*, char*) ()
#4  0x081944bc in log4cpp::Category::logva(int, char const*, char*) ()
#5  0x08066b1a in utils::Logger::logva (level=0, stringFormat=0x81b2740 "insert coin earn record failed", arguments=@0xb6965a3c: 0xb6965a54 ",%\033\b|")
    at src/logger.cpp:86
#6  0x08066b93 in utils::Logger::fatal (stringFormat=0x81b2740 "insert coin earn record failed") at src/logger.cpp:96

后来自己排查了一下,觉得这个还蛮有意思的一个问题。

问题的原因在于,由于习惯于在64位的系统上写代码,所以习惯于认为%ld就是打印一个64位的整形。
代码中想用%ld打印64位的request.log_id,在64位的系统中没问题。
但在32为的系统中%ld表示long其实只是32位的整形。于是在格式化输出的时候就出现了问题。
假设一个字母x表示一个32位,那么代码中的格式化输出函数在调用的时候内存是这么排的
x x x x
前两个x表示第一个参数,第二个x表示第二个参数,第三个x表示第三个参数。
那么由于%ld是32位的整形,于是在格式化输出的时候,对于第一个输出格式,第一个x被拿走了,
然后在输出第二个格式的时候,拿第二个x,这个时候虽然结果不对,但是程序未必会crash。
但在输出第三个格式的时候,由于第三个格式是字符串,本来应该取第四个x作为字符串的首地址,
但是由于我们在打第一个格式的时候误吧64位认为是32位,导致第三个格式在输出的时候没法取到正确的地址值,
然后由于访问这个非法地址,程序出core。


当然真正的栈帧情况未必就如我所说,但原理是这样的。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章