Странные проблемы с привязкой библиотеки C на Mac — ошибка сегментации

#macos #c #segmentation-fault

#macos #c #ошибка сегментации

Вопрос:

У меня странная ошибка сегментации, которая не существует, когда все находится в файле 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 %lun", sizeof collection);
  puts("FOO");
  int len = collection->len  ;
  puts("BAR");
  collection->items[len] = p;
}
  

пример2.c:

По сути, это тестовый файл:

 #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 %lun", 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:

Ваша проблема заключается в 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 , что объект определен где-то еще.

Комментарии:

1. В дополнение к вашему объяснению, нет предупреждения о времени привязки, поскольку collection оно определено как в исполняемом файле, так и в динамически связанной библиотеке… определенно 2 отдельных объекта

2. @chqrlie: Хорошее дополнение, добавлено к ответу.

3. Я попробовал macOS X, у меня была та же ошибка сегментации. Имея только объявление коллекции в файле заголовка и объявив его в exemple.c , выполнение нормально. Конечно, за голосование ^^