Простая проверка неразрешенных символов в общих библиотеках?


Я пишу довольно большую библиотеку общих объектов C++ и столкнулся с небольшой проблемой, которая делает отладку болью:

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

Я ищу простой способ проверить, есть ли у меня все символы, которые мне нужны во время компиляции, возможно, что-то, что я могу добавить в свой Makefile.

одно решение, которое я придумал, это запустить скомпилированную библиотеку через nm -C -U для получения списка откорректированные все неопределенные ссылки. Проблема в том, что это также придумывает список всех ссылок, которые находятся в других библиотеках, таких как GLibC, что, конечно,будет связано с этой библиотекой, когда окончательное приложение будет собрано. Можно было бы использовать вывод nm to grep через все мои заголовочные файлы и посмотреть, если любое из имен, соответствующих.. но это кажется безумием. Конечно, это не редкий вопрос, и есть лучший способ его решения?

4 70

4 ответа:

Проверьте опцию компоновщика -z defs/--no-undefined. При создании общего объекта это приведет к сбою ссылки, если есть неразрешенные символы.

если вы используете GCC для вызова компоновщика, вы будете использовать компилятор -Wl опция для передачи опции компоновщику:

gcc -shared ... -Wl,-z,defs

в качестве примера, рассмотрим следующий файл:

#include <stdio.h>

void forgot_to_define(FILE *fp);

void doit(const char *filename)
{
    FILE *fp = fopen(filename, "r");
    if (fp != NULL)
    {
        forgot_to_define(fp);
        fclose(fp);
    }
}

теперь, если вы построите это в общий объект, он будет успешным:

> gcc -shared -fPIC -o libsilly.so silly.c && echo succeeded || echo failed
succeeded

но если добавить -z defs, ссылка выйдет из строя и расскажет вам о вашем недостающем символе:

> gcc -shared -fPIC -o libsilly.so silly.c -Wl,-z,defs && echo succeeded || echo failed
/tmp/cccIwwbn.o: In function `doit':
silly.c:(.text+0x2c): undefined reference to `forgot_to_define'
collect2: ld returned 1 exit status
failed

на Linux (который вы используете) ldd -r a.out должен дать вам именно тот ответ, который вы ищете.

обновление: тривиальный способ создания a.out против чего проверить:

 echo "int main() { return 0; }" | g++ -xc++ - ./libMySharedLib.so
 ldd -r ./a.out

Как насчет testsuite ? Вы создаете фиктивные исполняемые файлы, которые ссылаются на нужные вам символы. Если связывание не удается, это означает, что интерфейс библиотеки является неполным.

однажды у меня была такая же проблема. Я разрабатывал компонентную модель на C++, и, конечно же, компоненты должны загружаться во время выполнения динамически. На ум приходят три решения, которые я применил:

  1. потребуется некоторое время, чтобы определить систему сборки, которая может компилироваться статически. Вы потеряете некоторое время на его разработку, но это сэкономит вам много времени, поймав эти раздражающие ошибки во время выполнения.
  2. группируйте свои функции в известных и понятных разделах, таким образом, вы можете группировать функции/заглушки, чтобы убедиться, что каждая соответствующая функция имеет свою заглушку. Если вы потратите время на документирование его хорошо, вы можете написать, возможно, сценарий, который проверяет определения (например, через его комментарии doxygen) и проверяет соответствующие .cpp-файл для его.
  3. сделайте несколько тестовых исполняемых файлов, которые загружают один и тот же набор библиотек, и укажите флаг RTLD_NOW для dlopen (если вы находитесь под *NIX). Они будут сигнализировать о пропаже атрибутика.

надеюсь, что это поможет.