Возможно, я ошибаюсь из-за того, как работает динамическое связывание, потому что не могу понять это. Как я понял, когда библиотека динамически связана, ее символы разрешаются во время выполнения. От этот ответ:
При динамическом связывании указатель на файл, на который
имя файла файла, например) входит в исполняемый файл и
содержимое указанного файла не включено во время ссылки. Это только
когда вы позже запустите исполняемый файл, который эти динамически связанные файлы
куплены в, и они только купили в копию в памяти
исполняемый, а не тот, что на диске.
[...]
В динамическом случае основная программа связана со средой выполнения C
библиотека импорта (то, что объявляет, что находится в динамической библиотеке
но на самом деле не определяет это). Это позволяет компоновщику связывать даже
хотя фактический код отсутствует.
Затем во время выполнения загрузчик операционной системы выполняет позднюю привязку
основная программа с библиотекой времени выполнения C (динамическая библиотека или
общая библиотека или другая номенклатура).
Я не совсем понимаю, почему g++
, похоже, ожидает, что общий объект будет присутствовать при динамическом связывании с ним. Конечно, я ожидаю, что имя библиотеки будет необходимо, чтобы ее можно было загрузить во время выполнения, но почему это .so
необходимо на этом этапе? Кроме того, g++
жалуется на неопределенные ссылки при ссылках на библиотеку.
Мои вопросы:
- Почему
g++
, по-видимому, требует совместно используемого объекта при динамическом связывании с ним, если загрузка библиотеки происходит только во время выполнения? Я понимаю, как может потребоваться флаг -l
для указания имени общего объекта, чтобы его можно было загружать во время выполнения, но я не вижу смысла указывать путь к .so
во время ссылки (-L
) или сам .so
.
- Почему
g++
пытается разрешить символы при динамическом связывании? Ничто не мешает мне получить полный .so
во время соединения, но затем предоставить другое (неполное) .so
во время выполнения, что вызывает сбой программы при попытке использовать неопределенный символ.
Я сделал воспроизводимый пример:
Структура каталогов:
.
├── main.cpp
└── test
├── usertest.cpp
└── usertest.h
Содержимое файла:
тест / usertest.h
#ifndef USERTEST_H_4AD3C656_8109_11E8_BED5_5BE6E678B346
#define USERTEST_H_4AD3C656_8109_11E8_BED5_5BE6E678B346
namespace usertest
{
void helloWorld();
// This method is not defined anywhere
void byeWorld();
};
#endif /* USERTEST_H_4AD3C656_8109_11E8_BED5_5BE6E678B346 */
тест / usertest.cpp
#include "usertest.h"
#include <iostream>
void usertest::helloWorld()
{
std::cout << "Hello, world\n";
}
main.cpp
#include "test/usertest.h"
int main()
{
usertest::helloWorld();
usertest::byeWorld();
}
Использование
$ cd test
$ g++ -c -fPIC usertest.cpp
$ g++ usertest.o -shared -o libusertest.so
$ cd ..
$ g++ main.cpp -L test/ -lusertest
$ LD_LIBRARY_PATH="test" ./a.out
Ожидаемое поведение
Я бы ожидал, что все будет зависать при попытке запустить a.out
, потому что он не может найти необходимые символы в libusertest.so
.
Фактическое поведение
Сборка a.out
завершается неудачно во время соединения, потому что не может найти byeWorld()
:
/tmp/ccVNcRRY.o: In function `main':
main.cpp:(.text+0xa): undefined reference to `usertest::byeWorld()'
collect2: error: ld returned 1 exit status