【问题标题】:C - Variadic function compiles but gives segmentation faultC - 可变参数函数编译但给出分段错误
【发布时间】:2016-12-20 23:02:54
【问题描述】:

我在 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

我们将不胜感激地接受任何想法。

【问题讨论】:

  • 您在实现该功能时遇到了错误。我建议先阅读<stdarg.h> 周围的相应手册。
  • 给我们足够的代码来复制错误。
  • 函数的声明在哪里?您的调用依赖于原型范围内 00.0 的隐式转换。省略调用函数的原型本质上是未定义的行为;在调用可变参数函数之前,您必须始终在范围内拥有原型。你的函数崩溃了,但你没有展示你是如何实现这个函数的。您希望我们如何帮助您调试它?你可能做错了很多事情;其中许多可能导致崩溃。请包含重现崩溃的 MCVE (minimal reproducible example)。
  • 您可以看到sizeData 没有对齐,这看起来更像是某个指针(0x402099)。如果这个函数在不同的翻译单元中声明,我的猜测是声明与定义不符。尝试为sec 传递类似3.14 的内容并再次调试。但首先,在启用所有警告的情况下编译您的程序。
  • @PeterCordes:GDB 提出的问题与这些问题类似。我更愿意从 OP 那里获取信息,但你的推断可能是正确的。

标签: c variadic


【解决方案1】:

根据 gdb 输出,调用者似乎没有它正在调用的函数的原型。正如@JonathanLeffler 注意到的那样,您写的是0 而不是0.0,所以它传递了一个整数,被调用者期望double


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

因此,如果调用者和被调用者在函数签名上存在分歧,他们将不同意哪个 arg 进入哪个寄存器,我认为这可以解释为什么 gdb 显示的 args 与调用者不匹配。


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

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

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

在不同的调用约定中(例如 -m32 带有堆栈参数),事情至少会很糟糕,因为这些参数将在堆栈上传递,但 intdouble 的大小不同。


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

您的函数可能仍会崩溃;谁知道您的代码中还有哪些未显示的错误?


当您的代码崩溃时,您应该查看它崩溃的 asm 指令以确定哪个指针是错误的——例如在 gdb 中运行 disas。即使您自己不理解它,包括在调试帮助问题中(连同寄存器值)也会有很大帮助。

【讨论】:

  • 谢谢彼得!我做了您列出的更改并完美运行(double 数字(duh),函数声明)。对不起,如果我之前不能回答,昨天是混乱的一天D:
猜你喜欢
  • 1970-01-01
  • 2018-02-12
  • 1970-01-01
  • 2015-07-18
  • 2011-06-29
  • 2020-08-24
  • 1970-01-01
  • 2019-07-27
  • 1970-01-01
相关资源
最近更新 更多