带-O3标志的分段错误

时间:2017-11-21 10:49:59

标签: c optimization clang stack-overflow

编辑:如果我浪费了你们这些人的时间,我真的很抱歉,发布这个问题时我的时间已经不多了。这里是我尽力减少它的代码

#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);
}

首先,程序因分段错误而崩溃,我找到了错误的访问代码行,这些代码行已在此代码演示中删除。我发现最初的错误是r中的变量main在与main_create_source无关的调用之后意外更改,这将被证明是这样(将此代码文件另存为{{1} })

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构建它。该错误只会在没有任何级别优化的情况下消失。

(以下是原帖,可以忽略。)

我有code file。使用-O3进行编译并使用cc -O0 futaba.c运行时,结果将为

./a.out futaba_test.ftb

(Zsh添加后缀0x7fba60c025b0 0x0 0x7fba60c025e0 0x7fba60c025b0 0x7fba60c02610 0x7fba60c025e0 0x7fba60c02640 0x7fba60c02610 0x7fba60c02670 0x7fba60c02640 0x7fba60c026b0 0x7fba60c02670 0x7fba60c026d0 0x7fba60c026b0 0x7fba60c02700 0x7fba60c026d0 0x7fba60c02730 0x7fba60c02700 main 0x7fba60c02730 main 0x7fba60c02730 A% )一切顺利。但是在使用%而不是-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函数中的堆栈溢出错误。

有什么问题?

1 个答案:

答案 0 :(得分:1)

这是很多代码,但这里至少有一面旗帜:

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

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

不知道优化编译器可以做些什么,但是一旦你将使用那个等待EOF的代码考虑在内......它会发臭。

注意:作为答案,这可能是不好的形式;但它指出了代码的具体问题。

此外,没有任何堆分配似乎有代码要求返回NULL;这也有点可怕。