LINUX.ORG.RU

undefined symbol статической библиотеки

 , , ,


1

2

Всем привет. У меня возникла следующая проблема:

есть библиотека mimetic с открытым кодом, собрал её как статическую libmimetic.a.

использую её в своем классе, который тоже собирается как статическая библиотека libparser.a.

этот класс является членом класса-обёртки для создания своего плагина libplugin.so.

А попытка загрузить плагин в основном приложении при помощи QPluginLoader заканчивается ошибкой «undefined symbol: _ZN7mimetic18ContentDisposition5labelE».

При помощи утилиты nm я поискал этот символ в созданных выходных файлах:

nm libmimetic.a:

contentdisposition.o:

0000000000000000 t _GLOBAL__sub_I__ZN7mimetic18ContentDisposition5labelE

...

0000000000000000 R _ZN7mimetic18ContentDisposition5labelE

...

header.o:

...

U _ZN7mimetic18ContentDisposition5labelE

nm libparser.a:

parser.o:

...

U _ZN7mimetic18ContentDisposition5labelE

nm libplugin.so:

U _ZN7mimetic18ContentDisposition5labelE

Не понимаю почему этот символ остается с пометкой undefined и как это изменить, если, конечно, это решит мою проблему. Буду рад любым конструктивным советам.

Проект пишу в Qt, компилятор GCC

Статические библиотеки чувствительны к порядку при линковке. Как линкуете? Ну и не страдайте фигнёй, используйте нормальные сошки.

slovazap ★★★★★
()

Где-то в описании своего проекта надо задать зависимость библиотеки parser от mimetic так чтобы эта зависимость пробрасывалась системой сборки до plugin. Как уже написали порядок линковки важен, но система сборки должна сама это разруливать.

PRN
()
Ответ на: комментарий от slovazap

Статические библиотеки чувствительны к порядку при линковке. Как линкуете?

Нет. Совсем не так. Порядок важен только тогда, когда вы хотите чтобы один и тот же символ был взят, например, из вашей, а не из стандартной библиотеки. В большинстве случаев символы определены взаимно в разных библиотеках, когда из огромной библиотеки выбирается только то, что хочет взять подбиблиотека.

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

vodz ★★★★★
()
Ответ на: комментарий от vodz

ТС юзает gcc, там по умолчанию порядок важен. https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

the linker searches and processes libraries and object files in the order they are specified. Thus, ‘foo.o -lz bar.o’ searches library ‘z’ after file foo.o but before bar.o. If bar.o refers to functions in ‘z’, those functions may not be loaded.

Вроде как, объединение в группы это исправляет или смена на линкер, которому порядок не важен.

У ТСа, с виду, при линковки libplugin.so не указано -lmimetic. Но без подробностей как идет сборка трудно сказать.

PRN
()
Ответ на: комментарий от PRN

там по умолчанию порядок важен

Этот пример не о том, объектники не совсем библиотеки, если речь про библиотеки, то их и надо собирать как библиотеки и там порядок не важен и даже невозможен, так как взаимная зависимость библиотек - везде и всюду.

vodz ★★★★★
()
Ответ на: комментарий от PRN

Забавно, что в gcc любой порядок прокатывал до версии 8.X.X. При переходе на 9-ю версию начались рвать подводные камни, пришлось местами менять порядок либ.

Oleg_Iu
()
Ответ на: комментарий от PRN

У ТСа, с виду, при линковки libplugin.so не указано -lmimetic.

Для статической mimetic.a это ведь ничего не даст.

vodz ★★★★★
()
Ответ на: комментарий от vodz

Нет. Совсем не так.

Именно так. Если линкуются две библиотеки и одна использует символы из другой, то они слинкуются только в одном порядке.

slovazap ★★★★★
()

undefined symbol: _ZN7mimetic18ContentDisposition5labelE

Скорее всего:

extern "C"
{
#include "bla-bla-bla.h"
}
anonymous
()
Ответ на: комментарий от slovazap

Именно так.

Не хотелось расписывать, но таки придётся. Если библиотека содержит несколько объектников, то символы будут последовательно выбраны и прилинкованы одним проходом просмотра библиотек. Но если символы в одном из объектников в каждой библиотеке, то все требуемые объектники будут заюзаны, а символы будут рекурсивно слинкованы, что ТСу и надо.

$ cat m.c
double my_cos1(double x);
double my_sin1(double x);

int main(void)
{
    double r = my_cos1(0) + my_sin1(0);
    return r != 0;
}

$ cat f1.c
#include <math.h>

double my_cos(double x)
{
    return cos(x);
}

double my_sin(double x);

double my_sin1(double x)
{
    return my_sin(x);
}

$ cat f2.c
#include <math.h>

double my_cos(double x);

double my_cos1(double x)
{
    return my_cos(x);
}

double my_sin(double x)
{
    return sin(x);
}
Как видите, в примере один объектник имеет my_X и my_Y1, а другой my_X1 и my_Y. Результат соберется в любой последовательности:
$ cc -c f1.c f2.c; ar rc l1.a f1.o; ar rc l2.a f2.o; ranlib f1.a; ranlib f2.a
$ cc m.c l1.a l2.a -lm # или
$ cc m.c l2.a l1.a -lm 
# причём -lm - в любом положении

vodz ★★★★★
()
Последнее исправление: vodz (всего исправлений: 1)
Ответ на: комментарий от vodz

Как видите

Молодец! Только вот это распарсь:

_ZN7mimetic18ContentDisposition5labelE

anonymous
()
Ответ на: комментарий от vodz

И зачем же ты врёшь?

echo 'int inner() { return 1; }' > inner.c
echo 'extern int inner(); int outer() { return inner(); }' > outer.c
echo 'extern int outer(); int main() { return outer(); }' > main.c
gcc -c -o inner.o inner.c && ar rc libinner.a inner.o && ranlib libinner.a
gcc -c -o outer.o outer.c && ar rc libouter.a outer.o && ranlib libouter.a
gcc -L. main.c -louter -linner && echo OK || echo FAIL
gcc -L. main.c -linner -louter && echo OK || echo FAIL
OK
/usr/local/bin/ld: ./libouter.a(outer.o): in function `outer':
outer.c:(.text+0xa): undefined reference to `inner'
collect2: error: ld returned 1 exit status
FAIL
slovazap ★★★★★
()
Ответ на: комментарий от Oleg_Iu

Не думаю, что в GCC 8 что-то меняли, это не какое-то новое поведение. Обычно разница в поведении объясняется различиями в параметрах по умолчанию в разных дистрибутивах, так что некоторые просто не сталкиваются пока умолчания не поменяют.

xaizek ★★★★★
()
Ответ на: комментарий от slovazap

И зачем же ты врёшь?

Я дал полное объяснение с примером, в чём проблема у ТСа, беретё, выполняете и не врёте.

vodz ★★★★★
()
Ответ на: комментарий от vodz

Описание того как линкер якобы работает вообще не понял (хотя я знаю как). Так что просто переделал пример, чтобы это было видно, а не скрывалось зависимостями от m.c к обоим объектным файлам в библиотеках:

$ cat build.sh
#!/bin/bash

cc -c f11.c f12.c f21.c f22.c
ar rc l1.a f11.o f12.o
ar rc l2.a f21.o f22.o
ranlib l1.a; ranlib l2.a

echo cc m.c l1.a l2.a -lm
cc m.c l1.a l2.a -lm
echo cc m.c l2.a l1.a -lm
cc m.c l2.a l1.a -lm

$ cat f11.c
double my_sin(double x);

double my_sin1(double x)
{
    return my_sin(x);
}

$ cat f12.c
#include <math.h>

double my_cos(double x)
{
    return cos(x);
}

$ cat f21.c
double my_cos(double x);

double my_cos1(double x)
{
    return my_cos(x);
}

$ cat f22.c
#include <math.h>

double my_sin(double x)
{
    return sin(x);
}

$ cat m.c
double my_cos1(double x);
double my_sin1(double x);

int main(void)
{
    double r = my_cos1(0) + my_sin1(0);
    return r != 0;
}

$ ./build.sh
cc m.c l1.a l2.a -lm
l2.a(f21.o): In function `my_cos1':
f21.c:(.text+0x1b): undefined reference to `my_cos'
collect2: error: ld returned 1 exit status
cc m.c l2.a l1.a -lm
l1.a(f11.o): In function `my_sin1':
f11.c:(.text+0x1b): undefined reference to `my_sin'
collect2: error: ld returned 1 exit status
xaizek ★★★★★
()
Ответ на: комментарий от xaizek

а не скрывалось зависимостями от m.c к обоим объектным файлам в библиотеках:

А это ведь и надо ТСу сделать. У него нет зависимости от символа в основном модуле, но есть в плагине, но загружается только плагин без подгрузки нужного символа. Отсюда и получается, что либо внести символ в объектник плагина либо сделать экпортируемым символом из основного бинаря, чтобы оно его захотело, каким угодно пустым хаком, ну там адрес получить и прочим stub-ом.

vodz ★★★★★
()
Ответ на: комментарий от vodz

А, так это был такой своеобразный вариант решения. Я не понял.

Раз плагин зависит от этой библиотеки, то ему её и тащить.

Второй вариант диковато смотрится. Приложение не должно выборочно включать те символы, которые используются в плагинах. Тогда уже логично всю библиотеку затащить через --whole-archive, если она большинству плагинов необходима.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Второй вариант диковато смотрится.

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

vodz ★★★★★
()
Ответ на: комментарий от xaizek

Много много лет компилил на всем, на чем может компилиться, от слакварей с центозами до дебианов, от фри всех версий до солярки, синологи, какие только можно роутеры. И тут мне вдруг присылают, что моя программа не компилится на убунте 20-й… Пришлось поменять местами $(LIBS) $(OBJS), которые шли в таком порядке больше 20 лет… Меня это удивило немного, но не сильно нарпягло. В убунте стоит gcc 9.x.x

Oleg_Iu
()

посмотри как люди подключают статитически стороние библиотеки в своих приложениях и сделай так же.

открываешь любой проект на гитхабе на qt и смотришь как они собирают и экспортируют символы

https://github.com/visualfc/liteide/tree/master/liteidex/src/3rdparty/ например тут

bhfq ★★★★★
()
Ответ на: комментарий от slovazap

Как линкуете?

я подозреваю, что процессом линковки управляет больше Qt, чем я, т.к. я только перечисляю в .pro-файле зависимые библиотеки, иногда указывая пути в которых надо их искать, при помощи переменной qmake LIBS.

Но не обращая внимание на это, у меня остается непонимание почему линковщик не пытается определить этот символ.

в файле contentdescription.h содержится объявление переменной:

namespace mimetic
{

/// Content-Description field value
struct ContentDescription: public FieldValue
{
    static const char label[];
...
}
}

которое добавляется в parser.cpp при подключении этого файла при помощи #include.

В одном из методов моего класса она используется следующим образом:

if (mime_entity.hasField(ContentDisposition::label))
    {...

В моем понимании, если бы я не использовал в своем коде mimetic::ContentDescription::label, то линковщик мог бы оставить символ этой переменной неразрешенным, а раз я эту переменную использую, то он должен найти определение, либо выдать ошибку, что ему это не удалось сделать, но видимо у меня проблемы с пониманием процесса сборки.

privet-romantik
() автор топика
Ответ на: комментарий от PRN

Вы оказались правы. После указания в pro-файл проекта, создающего плагин, библиотеки -lmimetic всё получилось. Но пришлось почему-то указывать не обычным способом:

LIBS += -L$${PWD}/libs/$${APP_PLATFORM}/release/lib -lmimetic
а указывать непосредственно файл:
LIBS += $${PWD}/libs/$${APP_PLATFORM}/release/lib/libmimetic.a
т.к. ругался ld и видимо по умолчанию искал so-файл (пока еще не пробовал, но возможно вместо LIBS надо воспользоваться PRE_TARGETDEPS).
И изменил порядок подключения библиотек в проект, создающий плагин:
LIBS += -L$${PWD}/libs/$${APP_PLATFORM}/release/lib -lparser
LIBS += $${PWD}/libs/$${APP_PLATFORM}/release/lib/libmimetic.a

privet-romantik
() автор топика
Ответ на: комментарий от privet-romantik

У Вас, возможно, проблемы в настройках проектов. В идеале должно быть так: в билд системе указывается что plugin зависит от parser, а parser зависит от mimetic. Для сборки plugin билд система прописывает где искать хиадеры обоих библиотек, а для линковки какие либы линковать и в каком порядке. Если все это делать руками, то можно упороться, особенно при развесистых зависимостях.

но как работает Qt’шная сборка я не знаю. Если верить этому https://wiki.qt.io/SUBDIRS_-_handling_dependencies то приблизительно как-то так

lib depends = lib2
app.depends = lib
PRN
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.