【问题标题】:vfprintf() throws SegFault - but input is well-defined?vfprintf() 抛出 SegFault - 但输入是明确定义的?
【发布时间】:2015-01-23 10:29:42
【问题描述】:

我在一个程序中被赋予了以下功能:

void log(char* arg1, ...)
{
    time_t      t;
    struct tm   *tm;
    va_list     args;
    char        *fmt;
    char        curtime[TIME_STR_SIZE];
    FILE        *fd;

    va_start(args, arg1); // linux stdarg va_start req 2 args - va_lilst, parm_n


    fmt = va_arg(args, char*);

    if (lfd == NULL)
        fd = stderr;
    else
        fd = lfd;

   (void) time(&t);
   tm = localtime(&t);

   sprintf(curtime, "%02d.%02d.%d, %02d:%02d:%02d", tm->tm_mday, tm->tm_mon+1, tm->tm_year+1900, tm->tm_hour, tm->tm_min, tm->tm_sec);

    fprintf(fd, "%s: ", curtime);
    vfprintf(fd, arg1, args);
    fprintf(fd, "\n");
    fflush(fd);

    va_end(args);
}

显然它需要输入参数列表并将它们写入文件。

但是运行这部分代码会引发分段错误,我已将其回溯到 vfprintf:

(gdb) backtrace
#0  0x00007ffff4799db2 in __strlen_sse2 () from /lib64/libc.so.6
#1  0x00007ffff476220d in vfprintf () from /lib64/libc.so.6
#2  0x0000000000401b03 in log (arg1=0x40529d "%s gestartet: PID = %d") at logging.c:73
#3  0x00000000004045d4 in main (argc=10, argv=0x7fffffffdc98) at abgleich.c:589

在运行 gdb 并打印参数时很好:

(gdb) print fd
$9 = (FILE *) 0x607010
(gdb) print fmt
$10 = 0x7fffffffe1a7 "/srv/workspace/abgleich/abgleich"
(gdb) print args
$11 = {{gp_offset = 16, fp_offset = 48, overflow_arg_area = 0x7fffffffda10, reg_save_area = 0x7fffffffd950}}

我很好奇为什么会抛出分段错误,因为在这种情况下它看起来是一个定义明确的输入。

函数的调用部分如下:

log("%s gestartet: PID = %d", argv[0], getpid());

编辑:

我使用 va_copy 重写了以下方式:

 va_start(args, arg1); // linux stdarg va_start req 2 args - va_lilst, parm_n
 va_copy(c_args, args);

 if (lfd == NULL)
     fd = stderr;
 else
     fd = lfd;

(void) time(&t);
tm = localtime(&t);

 /* strftime(curtime, TIME_STR_SIZE, "%d.%m.%y, %H:%M:%S\0", time_ptr); */
sprintf(curtime, "%02d.%02d.%d, %02d:%02d:%02d", tm->tm_mday, tm->tm_mon+1, tm->tm_year+1900, tm->tm_hour, tm->tm_min, tm->tm_sec);
 fprintf(fd, "%s: ", curtime);
 vfprintf(fd, arg1, c_args);
 fprintf(fd, "\n");
 fflush(fd);

 va_end(args);

【问题讨论】:

  • 编辑部分中的解决方案是正确的,但是如果这是您的完整代码,则不需要副本...如果以前需要,因为您使用了va_arg(),它修改了参数。但是现在没有va_arg() 电话,所以args == c_argsva_arg() 通常是一个宏,为了这个讨论,你可以这样想:#define va_list int*#define va_arg(argument, type) (type)(*argument++) - 这当然是一个巨大的简化,但最终的效果是相似的。

标签: c


【解决方案1】:

当您在从va_start() 获得的va_list 上使用va_arg()“函数”时,va_list 被修改,因此当传递给另一个函数时,它与您在开始时得到的不同。您可以将va_list 视为指针,使用va_arg() 就像增量运算符(++)。你这样调用你的函数:

log("%s gestartet: PID = %d", argv[0], getpid());

但是 vfprint 很可能是这样看的:

vfprintf(fd, "%s gestartet: PID = %d", /* argv[0], <-- REMOVED */ getpid());

【讨论】:

  • 我认为解决方案是在va_arg() 之前调用va_copy()
  • @user694733 - 或者根本不调用 va_arg() - 结果无论如何都不会使用(除了在 GDB 中调试和打印)(;但是复制原始的 va_list 将 - 当然 - 也解决问题。
  • 对,我没注意到fmt 没有被使用。
  • 好的 - 我想我明白你们俩所说的。所以我改用 fmt -> vfptintf(fd, fmt, args); 调用 vfprintf现在它似乎工作了:)
  • @Stefan - 我不确定我确切地知道你做了什么,但如果你只是在上面的代码中用fmt 替换了arg1,那么这个“工作”只是巧合 - 你将打印argv[0] 内容,但不是getpid() 的结果(并且您的原始格式将 被使用)...使用正确的解决方案 - 使用va_copy() 复制原始va_list(并将未修改的副本传递给vfprintf()),如果不需要,请不要使用va_arg()
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-02
  • 2012-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多