LINUX.ORG.RU

Вопрос по линковке C++

 , ,


0

2

Доброго,

есть библиотека А, которая использует библиотеки Б,В и Г. Я пишу прогу, которая использует библиотеку А. Линковщик ругается и хочет пути к библиотекам Б,В и Г. Почему? И как этого избежать?

P.S. g++4.8 qt5

/usr/bin/g++-4.8 -m64 -g -Wall -Wextra test_resultcomparator.cpp.o -o test_TestPluginResultComparator 
-Wl,-rpath-link=/lib/act -Wl,-rpath,$ORIGIN/../lib/act/,-rpath,$ORIGIN/../lib/act/plugins/
,-rpath,/usr/lib/x86_64-linux-gnu,-rpath,$ORIGIN,-rpath,lib/act/plugins/,-rpath,lib/act/ -L/usr/lib/x86_64-linux-gnu 
libResultComparator.so libact.so -lgcov -lQt5Core -lQt5Gui -lQt5PrintSupport -lQt5Script -lQt5Test -lQt5Widgets
/usr/bin/ld: warning: libqcustomplot.so.1, needed by libResultComparator.so, not found (try using -rpath or -rpath-link)

Например, просто сказать линкеру, где их искать. Или собрать их всех в одну большую библиотеку

Gvidon ★★★★
()

Ни линкер, ни библиотеки не могут сказать, где находится требуемый символ, поэтому он должен по всем ним пройти и разрешить имена, запомнив где какое, чтобы при запуске программы запросить лоадеру загрузку этих библиотек (лоадер знает где они лежат в рантайме, и это может не совпадать с путем при компиляции, но имя ему нужно в любом случае). Многие библиотеки используют pkg-config, который помогает указать все нужные для них зависимости для линкера, без твоего знания об этом.

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

Т.е. это нормальное поведение, что мне нужно все пути указывать. Понятно.

Rubbiroid
() автор топика

есть библиотека А, которая использует библиотеки Б,В и Г. Я пишу прогу, которая использует библиотеку А. Линковщик ругается и хочет пути к библиотекам Б,В и Г. Почему? И как этого избежать?

укажи путь к нестандартной либе.

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

Это я понял. Но пока еще не совсем понятно, «а нафига?». Либа А уже скомпилена и слинкована, она знает что и где брать. Нафига моей проге, которая использует функции только из либы А, знать, где лежат зависимости либы А?

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

Нафига моей проге, которая использует функции только из либы А, знать, где лежат зависимости либы А?

AFAIK линкер не знает, что ты будешь использовать только общие символы А. У него не дерево зависимостей, а просто Over9000 имён. Т.е. ты _можешь_ использовать что-то из Б,В,Г. Вот make AFAIK таки знает, и не будет лишний раз собирать Б,В,Г, а ld — увы.

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

Задача линкера связать неопределенные имена внутри объектника с именами в списке либ. Посему мне не понятно, зачем нужны еще и либы-зависимости. Может я что не так делаю при компиляции либы А?

Rubbiroid
() автор топика
Ответ на: комментарий от gh0stwizard

Да точно ты прав, просто скопировал имя библиотеки и не заметил.

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

Задача линкера связать неопределенные имена внутри объектника с именами в списке либ.

дык «список имён либы А» содержит ещё и Б,В,Г, вот оно так и получается. Может либа А так криво сделана, а возможно ты действительно используешь что-то из Б(думая, что это в А. а на самом деле, в А такого нет). Я не знаю, это там как-то можно посмотреть, но я давно не заморачивался... Не бери в голову.

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

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

QCustomPlot скомпилен как .so. К моей либе подключается так:

    cpp.includePaths: [project.act_extlibs_path + "qcustomplot/include/"]
    cpp.libraryPaths: [project.act_extlibs_path + "qcustomplot/"]
    cpp.dynamicLibraries: base.concat("qcustomplot")

Либа экспортирует свои инклюды и rpath:

    Export {
        Depends { name: "cpp" }
        cpp.includePaths: "."
	cpp.rpaths: project.act_plugin_path
    }
В project.act_plugin_path Находится путь, где лежит готовая либа. Тест использует либу так:
Depends { name: "Result Comparator" }

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

Если в либе заэкспортить еще и это:

    Export {
        cpp.libraryPaths: product.cpp.libraryPaths
        cpp.dynamicLibraries: product.cpp.dynamicLibraries
    }
То линкер не ругается, что и очевидно, т.к. ему явно указано, где брать qcustomplot.

Но мне не понятно, нафига нужны зависимости либы, которая уже скомпилена и слинкована к моменту линковки тестов?

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

Вау, как все сложно в этом вашем qbs!

Но мне не понятно, нафига нужны зависимости либы, которая уже скомпилена и слинкована к моменту линковки тестов?

Могу только предложить тебе посмотреть через objdump, ldd на твои библиотеки.

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

Не, это на первый взгляд все сложно. Вот например сборка плагина:

import qbs 1.0
import "../plugin.qbs" as Plugin
Plugin {
    name: "Plugin 1"

    Depends { name: "Qt"; submodules: ["xy"] }
    Depends { name: "ABC" }
    
    files: [
        "*.h",
        "*.cpp"
    ]
}
А в plugin.qbs находятся базовые вещи, общие для всех плагинов.

Могу предложить только посмотреть через objdump, ldd на свои библиотеки.

Ну, если я добавляю customplot в зависимости теста, то он там и появляется.

Rubbiroid
() автор топика

Единственное для чего это может быть нужно, имхо, если гцц генерит код для загрузки либ в исполняемом файле и грузит все сразу. Тогда понятно, зачем нужно указывать все зависимости. Может кто знает, так ли это?

Rubbiroid
() автор топика

-shared для dynamic linking (еслі такое позволяет задача)

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

это так. Можно лінковать .so - тогда нужна будет только libA.so

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

а нет - туплю. у тебя so лінкуется. Возможно там лезет что-то в exported symbols.

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

есть варіант что оно не может загрузіть саму .so для линковки потому что нет либы на которую она депендается. Проверь что либа такая есть.

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

Я это понимаю. Если путь явно прописать, то все нормально. Меня больше волнует вопрос «нафига?» и «почему?». Т.е. нафига линкеру вообще нужны зависимости и почему так было сделано. Ну или, почему у меня так получается.

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

Возможно это детали реализации ld. Технически оно вроде как и не требуется - даже сама либа в принципе не нужна (всё можно достать из деклараций). Хто знает - подскажите. :)

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

А не - оно нужно ибо в Цэ(++) нет модулей. Т.е. либы нужно загрузить чтобы проверить в какой конкретно либе есть нужный символ. А чтобы загрузить - ld хочет и dependencies.

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

Так в том и загвоздка, я использую классы из libResultComparator.so, которая использует другие либы. И линкеру нужны эти другие либы, вот только нафига, мне не понятно. Возможно, у меня кривые хидеры и поэтому тянутся другие либы. Будет время, уберу все внутренности в приватный класс, авось пропадут зависимости.

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

И линкеру нужны эти другие либы, вот только нафига, мне не понятно.

Моя гипотеза - другие либы нужны, потому что ld так написан (то есть что это багофича ld). Переписывания своего кода не помогут 100% - можешь не пытаться.

dzidzitop ★★
()

А подумать? Для линкера существует единое глобальное пространство имен. Кроме проверки на наличие определения используемого имени линкер также осуществляет проверку на дублирование определения одного и того же имени. Если бы линкер этого не делал, то была бы возможна ситуация, когда линковка прошла успешно, а загрузка бы падала из-за конфликта определений.

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

А сам подумать? То, что ты описал, возможно только в том случае, если в двух либах, которые я использую, есть одинаковые нэймспесы и классы. Только эта ситуация ловится компилятором. А если линкер все имена скидывает в одну кучу, то это баг линкера.

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

есть одинаковые нэймспесы и классы.

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

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

Серьезно? Ну добавь два одинаковых класса и попробуй их в третьем использовать.

в C++ используется сишный линкер

Да хоть от турбопаскаля. Суть не меняется от этого. Он хочет все дерево зависимостей, которое нафиг не сдалось ему.

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

Ну добавь два одинаковых класса и попробуй их в третьем использовать.

Естественно в разных единицах трансляции. Ты ведь знаешь что это такое?

so.h:

struct A {
  static void print();
};

so1.cpp:

#include <iostream>
#include "so.h"

void A::print()
{
  std::cout << "A1\n";
}

so2.cpp:

#include <iostream>
#include "so.h"

void A::print()
{
  std::cout << "A2\n";
}

so3.cpp:

#include "so.h"

int main()
{
  A::print();
}

Собираем

g++ so1.cpp so2.cpp so3.cpp

И, закономерно, получаем ошибку

/tmp/cctMrVES.o: In function `A::print()':
so2.cpp:(.text+0x0): multiple definition of `A::print()'
/tmp/ccDESdqY.o:so1.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

Заметь, ошибку нашел ld!

Что тоже самое будет и с so-шками, проверишь сам.

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

Он хочет все дерево зависимостей, которое нафиг не сдалось ему.

У тебя телега впереди лошади: не зависимости определяют компановку, а компановка — зависимости.

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

Перечитай тред, ты никак проблему не поймешь. Линкер хочет иметь все библиотеки, в том числе и зависимости.

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

А ты никак не поймешь ответ.

Хочет он потому, что ему надо, а зачем ему надо, уже было сказано.

И вообще сначала читай доки, а потом заявляй про баг. Доки про форматы объектных файлов, про единицы трансляции, про разделяемые библиотеки, про компоновку и др.. Гугл тебе в помощь.

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

А ты никак не поймешь ответ.

Видимо так.

Вот у меня есть объектник и я говорю линкеру «Вот файлик, вот либы, которые я использую, связывай». Объектник будет вершиной дерева (упрощенно), либы первым уровнем. Зачем линкеру проверять следующий уровень? В чем проблема иметь два одинаковых класса на разных уровнях дерева? Надеюсь я доступно описал ситуацию.

З.Ы. Ситуацию с одинаковыми классами и компилятором ты понял неправильно. Я описывал вот что:

Либа А:
classA.h
classA.cpp
Либа Б:
classA.h
classA.cpp
Исполняемый модуль:
#include <classA.h>
Либа А и Б друг с другом никак не связаны. При компиляции исполняемого файла оба хидера у него в includepath.

Rubbiroid
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.