C - Variadic函数编译但给出了分段错误

时间:2016-12-20 23:02:55

标签: c variadic

我在C中有一个可变参数函数来写入日志文件,但是一旦调用它就会在标题中出现分段错误。

在主要流程中,调用具有以下格式:

mqbLog("LOG_INFORMATION",0,0,"Connect",0,"","Parameter received");  

并且函数以这种方式定义:

void mqbLog(char *type,
            int  numContext,
            double sec,
            char *service,
            int   sizeData,
            char *data,
            char *fmt,
            ...
            )
{  
    //write the log in the archive
}

编译好。当我调试进程时,对mqbLog函数的调用已经完成,它在函数的开括号中给出了分段错误,所以我可以询问函数值:

(gdb) p type
$1 = 0x40205e "LOG_INFORMATION"
(gdb) p numContext
$2 = 0
(gdb) p sec
$3 = 0
(gdb) p service
$4 = 0x0
(gdb) p sizeData
$5 = 4202649
(gdb) p data
$6 = 0x0

任何想法都将被感激不尽。

1 个答案:

答案 0 :(得分:2)

根据gdb输出,看起来调用者没有调用它的函数的原型。正如@JonathanLeffler注意到的那样,你写了0而不是0.0,所以它传递了一个整数,其中被调用者期待double

从指针值判断,这可能是在具有System V调用约定的x86-64 Linux上,其中为arg分配的寄存器由例如它决定。第三个整数 arg。 (有关ABI /调用约定文档,请参阅 wiki)。

因此,如果调用者和被调用者不同意函数签名,他们会不同意哪个arg在哪个寄存器中,我认为这解释了为什么gdb显示的args与调用者不匹配。

在这种情况下,调用者将"Connect"(地址)放在RCX中,因为它是带有该隐式声明的第4个整数/指针arg。

调用者在RDX中查找service的值,因为它的调用者是第3个整数/指针arg。

sec显然是偶然的0.0。它只是使用XMM0中的任何东西。或者可能未初始化的堆栈空间,因为调用者将设置AL=0以指示寄存器中没有传递FP args(仅对于可变参数函数是必需的)。注意al = fp寄存器args的数量包括原型可用时固定的非可变参数args。使用编译您的原型可用原型包括mov eax, 1之前的call。请参阅source + asm以使用/不使用原型on the Godbolt compiler explorer进行编译。

在一个不同的调用约定中(例如带有堆栈args的-m32),事情会至少破坏,因为这些args会在堆栈上传递,但intdouble是不同的尺寸。

为FP args写0.0会使隐式声明与定义匹配。 但不要这样做,调用未声明的功能仍然是一个糟糕的主意。使用-Wall让编译器告诉您代码何时做坏事。

你的功能可能仍会崩溃;谁知道你在代码中没有显示的其他错误?

当你的代码崩溃时,你应该查看它崩溃的asm指令,找出哪个指针不好 - 例如在gdb中运行disas。即使你自己不了解它,包括调试帮助问题(以及寄存器值)也可以帮助很多。