C - Variadic 函数编译但给出分段错误
C - Variadic function compiles but gives segmentation fault
我在 C 中有一个可变参数函数可以写入日志文件,但是一旦调用它,它就会在 header.
中给出一个分段错误
在主进程中,调用格式如下:
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
= 0x40205e "LOG_INFORMATION"
(gdb) p numContext
= 0
(gdb) p sec
= 0
(gdb) p service
= 0x0
(gdb) p sizeData
= 4202649
(gdb) p data
= 0x0
如有任何想法,我们将不胜感激。
根据 gdb 的输出,调用者似乎没有所调用函数的原型。正如@JonathanLeffler 所注意到的,您写的是 0
而不是 0.0
,因此它传递了一个整数,被调用方期望 double
.
从指针值判断,这可能是在 x86-64 Linux 上使用 System V 调用约定,其中分配给 arg 的寄存器由它决定,例如第三个 整数 参数。 (请参阅 x86 维基以获取 ABI/calling 公约文档)。
因此,如果调用者和被调用者不同意函数签名,他们将不同意哪个 arg 进入哪个寄存器,我认为这解释了为什么 gdb 显示与调用者不匹配的 args。
在这种情况下,调用者将 "Connect"
(地址)放入 RCX,因为它是具有该隐式声明的第 4 个 integer/pointer arg。
调用者在 RDX 中查找 service
的值,因为其调用者的第 3 个 integer/pointer arg.
sec
在被调用者中显然是 0.0 是偶然的。它只是使用 XMM0 中的任何内容。或者可能 可能 未初始化的堆栈 space,因为调用者会设置 AL=0
以指示没有 FP args 被传递到寄存器中(仅可变参数函数需要)。注意 al
= 当原型可用时,fp 寄存器参数的数量包括固定的非可变参数。使用 编译您的调用 可用原型包括 call
之前的 mov eax, 1
。参见 source+asm 编译 with/without 原型 on the Godbolt compiler explorer.
在不同的调用约定中(例如 -m32
与堆栈参数),事情至少会严重中断,因为这些参数将在堆栈上传递,但是 int
和 double
尺寸不同。
为 FP args 编写 0.0
将使隐式声明与定义匹配。 但是不要这样做,调用未声明的函数仍然是一个糟糕的主意。使用 -Wall
让编译器在你的代码做坏事时告诉你。
您的功能可能仍会崩溃;谁知道您在未显示的代码中还有哪些其他错误?
当你的代码崩溃时,你应该查看它崩溃的 asm 指令,以确定哪个指针是坏的——例如运行 disas
在 gdb 中。即使您自己不理解,将其包含在调试帮助问题中(以及寄存器值)也会有很大帮助。
我在 C 中有一个可变参数函数可以写入日志文件,但是一旦调用它,它就会在 header.
中给出一个分段错误在主进程中,调用格式如下:
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
= 0x40205e "LOG_INFORMATION"
(gdb) p numContext
= 0
(gdb) p sec
= 0
(gdb) p service
= 0x0
(gdb) p sizeData
= 4202649
(gdb) p data
= 0x0
如有任何想法,我们将不胜感激。
根据 gdb 的输出,调用者似乎没有所调用函数的原型。正如@JonathanLeffler 所注意到的,您写的是 0
而不是 0.0
,因此它传递了一个整数,被调用方期望 double
.
从指针值判断,这可能是在 x86-64 Linux 上使用 System V 调用约定,其中分配给 arg 的寄存器由它决定,例如第三个 整数 参数。 (请参阅 x86 维基以获取 ABI/calling 公约文档)。
因此,如果调用者和被调用者不同意函数签名,他们将不同意哪个 arg 进入哪个寄存器,我认为这解释了为什么 gdb 显示与调用者不匹配的 args。
在这种情况下,调用者将 "Connect"
(地址)放入 RCX,因为它是具有该隐式声明的第 4 个 integer/pointer arg。
调用者在 RDX 中查找 service
的值,因为其调用者的第 3 个 integer/pointer arg.
sec
在被调用者中显然是 0.0 是偶然的。它只是使用 XMM0 中的任何内容。或者可能 可能 未初始化的堆栈 space,因为调用者会设置 AL=0
以指示没有 FP args 被传递到寄存器中(仅可变参数函数需要)。注意 al
= 当原型可用时,fp 寄存器参数的数量包括固定的非可变参数。使用 编译您的调用 可用原型包括 call
之前的 mov eax, 1
。参见 source+asm 编译 with/without 原型 on the Godbolt compiler explorer.
在不同的调用约定中(例如 -m32
与堆栈参数),事情至少会严重中断,因为这些参数将在堆栈上传递,但是 int
和 double
尺寸不同。
为 FP args 编写 0.0
将使隐式声明与定义匹配。 但是不要这样做,调用未声明的函数仍然是一个糟糕的主意。使用 -Wall
让编译器在你的代码做坏事时告诉你。
您的功能可能仍会崩溃;谁知道您在未显示的代码中还有哪些其他错误?
当你的代码崩溃时,你应该查看它崩溃的 asm 指令,以确定哪个指针是坏的——例如运行 disas
在 gdb 中。即使您自己不理解,将其包含在调试帮助问题中(以及寄存器值)也会有很大帮助。