【问题标题】:Segmentation fault with -O3 flag [closed]带有-O3标志的分段错误[关闭]
【发布时间】:2017-11-21 10:49:59
【问题描述】:

编辑:如果我浪费了你们的时间,我真的很抱歉,我在发布这个问题时已经没有时间了。这是我已尽力将其最小化的代码

#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

enum Error {
  ERROR_UNRESOLVED_NAME = 1,
  ERROR_CANNOT_OPEN_FILE,
  ERROR_NO_ARGV,
  ERROR_UNRECOGNIZED_SYMBOL,
  ERROR_UNCOMPLETED_SENTENCE,
  ERROR_RECURSIVE_SELF
};

struct _Piece;
typedef struct _Piece *(*PieceFunc)(struct _Piece *, void *);
struct _Piece {
  PieceFunc function;
  void *backpack;
};
typedef struct _Piece Piece;

Piece *piece_create(PieceFunc func, void *pack) {
  Piece *piece = malloc(sizeof(Piece));
  piece->function = func;
  piece->backpack = pack;
  return piece;
}

typedef struct _Record {
  char *name;
  int name_len;
  Piece *piece;
  struct _Record *previous;
} Record;

Record *record_register(Record *pre, char *name, int name_len, Piece *piece) {
  Record *record = malloc(sizeof(Record));
  record->name = name;
  record->name_len = name_len;
  record->piece = piece;
  record->previous = pre;
  return record;
}

typedef struct {
  char *file_name;
  char *source;
  int length;
  int current;
  int line;
  int column;
} Source;

Source *source_create(char *s, int len, char *file_name) {
  Source *source = malloc(sizeof(Source));
  source->source = s;
  source->file_name = file_name;
  source->length = len;
  source->current = 0;
  source->line = source->column = 1;
  return source;
}

Piece *apply(Piece *caller, Piece *callee) {
  return caller->function(callee, caller->backpack);
}

// Part 3, internals
Piece *internal_self(Piece *callee, void *backpack) {
  if (callee->function == internal_self) {
    fprintf(stderr,
            "recursive `self` calling between two pieces\n"
            "piece 1 backpack: %p\n"
            "piece 2: %p backpack: %p",
            backpack, callee, callee->backpack);
    exit(ERROR_RECURSIVE_SELF);
  }
  return apply(callee, piece_create(internal_self, backpack));
}

Piece *internal_put(Piece *callee, void *backpack) {
  int *p_char = callee->backpack;
  putchar(*p_char);
  return piece_create(internal_self, NULL);
}

Source *main_create_source(char *file_name) {
  FILE *source_file = fopen(file_name, "r");
  if (!source_file) {
    fprintf(stderr, "cannot open file \"%s\"\n", file_name);
    exit(ERROR_CANNOT_OPEN_FILE);
  }

  char *source = NULL;
  int length = 0;
  while (true) {
    char *line = NULL;
    int line_len = 0;
    line_len = (int)getline(&line, (size_t *)&line_len, source_file);
    if (line_len < 0) {
      break;
    }
    if (source == NULL) {
      source = line;
    } else {
      source = realloc(source, sizeof(char) * (length + line_len + 1));
      strcat(source, line);
      // free(line);
    }
    length += line_len;
  }
  fclose(source_file);
  return source_create(source, length, file_name);
}

#define MAIN_REGISTER_INTERNAL(record, name, func)                             \
  record = record_register(record, name, sizeof(name) - 1,                     \
                           piece_create(func, NULL));                          \
  printf("%p %p\n", record, record->previous);

int main(int argc, char *argv[]) {
  if (argc < 2) {
    fprintf(stderr, "please specify source file by command line argument\n");
    exit(ERROR_NO_ARGV);
  }

  Record *r = NULL;
  MAIN_REGISTER_INTERNAL(r, "put", internal_put);
  printf("main %p\n", r);

  Source *s = main_create_source(argv[1]);

  printf("main %p\n", r);
}

起初,程序因分段错误而崩溃,我找到了访问错误的代码行,在此代码演示中已将其删除。我发现最初的错误是main 中的变量r 在对main_create_source 的不相关调用后会发生意外变化,这将如下所示(将此代码文件保存为foo.c)

$ cc -O0 -g foo.c        
$ ./a.out futaba_test.ftb
0x7fc0024025b0 0x0
main 0x7fc0024025b0
main 0x7fc0024025b0
$ cc -O3 -g foo.c        
$ ./a.out futaba_test.ftb
0x7fe861c025b0 0x0
main 0x7fe861c025b0
main 0x7fe800000000

更改优化级别时行为会发生变化。它与EOF 无关,因为我已将其删除,并且我认为strcat 的目的地的内存足够丰富。感谢您的帮助。

顺便如果有什么需要指出这个sn-p的目的。这是我正在研究的最小语言的解释器。它当时能够评估小型源代码 sn-p,这是我第一次尝试使用 -O3 构建它。没有任何关卡优化,bug只会消失。


(以下为原帖,可无视。)

我有这个code file。用cc -O0 futaba.c编译,用./a.out futaba_test.ftb运行,结果是

0x7fba60c025b0 0x0
0x7fba60c025e0 0x7fba60c025b0
0x7fba60c02610 0x7fba60c025e0
0x7fba60c02640 0x7fba60c02610
0x7fba60c02670 0x7fba60c02640
0x7fba60c026b0 0x7fba60c02670
0x7fba60c026d0 0x7fba60c026b0
0x7fba60c02700 0x7fba60c026d0
0x7fba60c02730 0x7fba60c02700
main 0x7fba60c02730
main 0x7fba60c02730
A%

(Zsh 添加后缀%)一切顺利。但是当使用-O3 而不是-O0 编译时,结果将是

0x7f8f274025b0 0x0
0x7f8f274025e0 0x7f8f274025b0
0x7f8f27402610 0x7f8f274025e0
0x7f8f27402640 0x7f8f27402610
0x7f8f27402670 0x7f8f27402640
0x7f8f274026b0 0x7f8f27402670
0x7f8f274026d0 0x7f8f274026b0
0x7f8f27402700 0x7f8f274026d0
0x7f8f27402730 0x7f8f27402700
main 0x7f8f27402730
main 0x7f8f00000000
[1]    27811 segmentation fault  ./a.out futaba_test.ftb

最后两行main打印不同的地址,第二行无效,导致后面record_resolve函数出现堆栈溢出错误。

有什么问题?

【问题讨论】:

  • 使用调试器查找导致段错误的行。
  • “有什么问题?” 1/ 你问了一个问题,是关于 we can't reproduce for some reason 的一些代码的问题。这让我有理由“投票结束”。 2/您忽略了解释总体目的或目标,以及编写此程序的原因,从而严重限制了您的问题的有用性。也许这是一个XY问题?在您向我们提供一些全面的信息之前,我们不会知道。 3/ 这个问题的答案几乎肯定需要理解一本书。你在读一本吗?如果有,是哪一个?
  • 对不起,我不会为你检查 440 行代码。使用调试器。
  • valgrind 可能对这类问题有用。
  • 请创建一个minimal reproducible example。我们不会为一个(可能)简单的问题编写数百行代码。

标签: c optimization clang stack-overflow


【解决方案1】:

代码很多,但这里至少有一个标志:

char source_fetch(Source *s) {
  return s->current == s->length ? EOF : s->source[s->current];
}

这会将EOF 强制转换为char,这是一个非常糟糕的主意。这就是为什么所有可以返回 EOF 的标准 C 函数(如 getchar() 返回 int。

不知道优化编译器能从中得到什么,但一旦你考虑到等待EOF 的代码使用它......它很臭。

注意:这可能是不好的答案;但它指出了代码的一个具体问题。

此外,似乎没有一个堆分配有代码正在寻找 NULL 被返回;这也有点吓人。

【讨论】:

    猜你喜欢
    • 2021-12-20
    • 1970-01-01
    • 1970-01-01
    • 2013-11-07
    • 2018-10-30
    • 2017-03-12
    • 2014-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多