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

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

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

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

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

Ответ 1

Проверьте вариант компоновщика -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

Ответ 2

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

UPDATE: тривиальный способ создания a.out, с помощью которого можно проверить:

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

Ответ 3

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

Ответ 4

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

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

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