Непонятные проблемы с библиотекой C на Mac - ошибка сегментации - PullRequest
3 голосов
/ 27 марта 2019

У меня странная ошибка сегментации, которая не существует, когда все находится в файле 1 .c, но существует, когда я помещаю часть кода в динамически связанную библиотеку и связываю ее с тестом. файл. Полный код для рабочего кода файла 1 .c находится внизу, полный код для системы ошибок с файлами 2 .c и 1 .h идет первым.

Вот система ошибок:

example.h

#include <stdio.h>
#include <stdlib.h>

typedef struct MYARRAY {
  int len;
  void* items[];
} MYARRAY;

MYARRAY *collection;

void
mypush(void* p);

example.c

#include "example.h"

void
mypush(void* p) {
  printf("Here %lu\n", sizeof collection);
  puts("FOO");
  int len = collection->len++;
  puts("BAR");
  collection->items[len] = p;
}

example2.c * +1018 *:

Это, по сути, тестовый файл:

#include "example.h"

void
test_print() {
  puts("Here1");
  mypush("foo");
  puts("Here2");
}

int
main() {
  collection = malloc(sizeof *collection + (sizeof collection->items[0] * 1000));
  collection->len = 0;
  puts("Start");
  test_print();
  puts("Done");
  return 0;
}

Makefile

Я связываю example с example2 здесь и запускаю:

example:
  @clang -I . -dynamiclib \
    -undefined dynamic_lookup \
    -o example.dylib example.c
  @clang example2.c example.dylib -o example2.o
  @./example2.o
.PHONY: example

Вывод:

$ make example
Start
Here1
Here 8
FOO
make: *** [example] Segmentation fault: 11

Но он должен показать полный вывод:

$ make example
Start
Here1
Here 8
FOO
BAR
Here2
Done

Странная вещь - все работает, если это система:

example.c

#include <stdio.h>
#include <stdlib.h>

typedef struct MYARRAY {
  int len;
  void* items[];
} MYARRAY;

MYARRAY *collection;

void
mypush(void* p) {
  printf("Here %lu\n", sizeof collection);
  puts("FOO");
  int len = collection->len++;
  puts("BAR");
  collection->items[len] = p;
}

void
test_print() {
  puts("Here1");
  mypush("foo");

  puts("Here");
}

int
main() {
  collection = malloc(sizeof *collection + (sizeof collection->items[0] * 1000));
  collection->len = 0;
  puts("ASF");

  test_print();

  return 0;
}

Makefile

example:
  @clang -o example example.c
  @./example
.PHONY: example

Хотите знать, почему это вызывает ошибку сегментации, когда она связана подобным образом, и что я делаю неправильно.

Я проверил otool и DYLD_PRINT_LIBRARIES=YES, и он показывает, что он импортирует динамически связанные библиотеки, но по какой-то причине происходит сбой сегментации при связывании, но работает нормально, когда он не связан.

1 Ответ

5 голосов
/ 27 марта 2019

Ваша проблема в этом, в example.h:

MYARRAY *collection;

Поскольку оба файла main.c и example.c включают этот файл, вы в итоге дважды определяете collection, что приводит к неопределенному поведению. Вы должны убедиться, что вы определяете каждый объект только один раз. Детали относительно не важны, так как с неопределенным поведением может случиться что угодно, но, вероятно, происходит то, что main.c выделяет память для одного объекта, а тот, который использует example.c, все еще NULL. Как упоминалось в комментариях, поскольку вы определяете collection в main.c, ваш компоновщик может создать исполняемый файл без необходимости искать этот символ в динамической библиотеке, поэтому вы не получите предупреждение о времени ссылки о его определении и там, и, очевидно, не будет причин для предупреждения во время компиляции библиотеки.

Это работает для вас, когда вы помещаете все в один файл, потому что, очевидно, вы больше ничего не определяете дважды. Сама ошибка не имеет ничего общего с тем фактом, что вы используете динамическую библиотеку, хотя это может затруднить ее обнаружение.

Было бы лучше определить это в example.c и предоставить функцию конструктора, для main() нет необходимости иметь прямой доступ к ней. Но если вы должны сделать это, то определите это в example.c и просто объявите идентификатор extern в заголовочном файле, чтобы сообщить main.c, что объект определен где-то еще.

...