Неопределенная ссылка на ошибку, когда библиотеки .so участвуют при создании исполняемого файла. - PullRequest
3 голосов
/ 24 января 2020

У меня есть библиотека .so, и при ее создании я не получил никаких неопределенных ошибок ссылок. Но теперь я создаю исполняемый файл с использованием файла .so и вижу неопределенные ошибки ссылок на этапе компоновки, как показано ниже:

xy.so: неопределенная ссылка на `MICRO_TO_NANO_ULL '

Я ссылался на это и это , но не мог по-настоящему понять динамическое связывание c.

Также чтение из здесь приводит к еще большей путанице:

Динамическое связывание c выполняется путем помещения имени разделяемой библиотеки в исполняемый образ. Реальное связывание с подпрограммами библиотеки не происходит до тех пор, пока не будет запущен образ, когда и исполняемый файл, и библиотека помещены в память. Преимущество динамического связывания c состоит в том, что несколько программ могут совместно использовать одну копию библиотеки.

Мои вопросы:

  • Не динамически c связывание означает, что когда я запускаю исполняемый файл, используя ./executable_name, тогда, если компоновщик не может найти файл .so, от которого зависит исполняемый файл, он должен создать sh?

  • Что на самом деле является Dynami c ссылки, если все ссылки на внешние объекты разрешаются при сборке? Это какая-то предварительная проверка, выполняемая динамическим компоновщиком c? Еще динамический компоновщик c может использовать LD_LIBRARY_PATH для получения дополнительных библиотек для разрешения неопределенных символов.

Ответы [ 4 ]

1 голос
/ 24 января 2020

Разве не динамическое связывание c означает, что когда я запускаю исполняемый файл, используя ./executable_name, тогда, если компоновщик не может найти файл .so, от которого зависит исполняемый файл, он должен создать sh?

Нет, компоновщик завершит работу с сообщением «Нет такого файла или каталога».

Представьте себе так:

  • Ваш исполняемый файл хранит где-то список общих необходимые библиотеки.
  • Линкер, думайте о нем как о обычной программе.
  • Линкер открывает ваш исполняемый файл.
  • Линкер читает этот список. Для каждого файла.
    • Он пытается найти этот файл в путях компоновщика.
    • Если он находит файл, он «загружает» его.
    • Если он не может найти файл, он получает errno с вызовом No Such file or directory из open(). А затем печатает сообщение о том, что он не может найти библиотеку и завершает ваш исполняемый файл.
  • При запуске исполняемого файла компоновщик динамически ищет символ в общих библиотеках.
    • Когда он не может найти символ, он печатает некоторое сообщение и исполняемый файл запускается.

Вы можете, например, установить LD_DEBUG=all, чтобы проверить, что линкер делает. Вы также можете проверить свой исполняемый файл в strace, чтобы увидеть все вызовы open.

Что на самом деле означает динамическое связывание c, если все ссылки на внешние сущности разрешаются при сборке?

Динамическое c связывание - это когда вы запускаете исполняемый файл, а компоновщик загружает каждую разделяемую библиотеку.

При сборке ваш компилятор достаточно любезен, чтобы проверить, что Все символы, которые вы используете в своей программе, существуют в общих библиотеках. Это только для безопасности. Например, вы можете отключить эту проверку с помощью ex. --unresolved-symbols=ignore-in-shared-libs.

Это какая-то предварительная проверка, выполняемая динамическим компоновщиком c?

Да.

Еще динамический компоновщик c может использовать LD_LIBRARY_PATH для получения дополнительных библиотек для разрешения неопределенных символов.

LD_LIBRARY_PATH - это просто список разделенных запятыми путей для поиска разделяемой библиотеки. Пути в LD_LIBRARY_PATH просто обрабатываются перед стандартными путями. Это все. Он не получает «дополнительные библиотеки», он получает дополнительные пути для поиска библиотек - библиотеки остаются прежними.

1 голос
/ 24 января 2020

Похоже, что при компиляции вашей общей библиотеки отсутствует #define. Эта ошибка

xy.so: неопределенная ссылка на `MICRO_TO_NANO_ULL '

означает, что должно присутствовать что-то вроде

#define MICRO_TO_NANO_ULL(sec) ((unsigned long long)sec * 1000)

, но not.

Компилятор предполагает, что это внешняя функция и создает для нее (неопределенный) символ, в то время как он должен быть разрешен во время компиляции макросом препроцессора.

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

1 голос
/ 24 января 2020

Не динамическое связывание c означает, что когда я запускаю исполняемый файл, используя ./executable_name, тогда, если компоновщик не может найти файл .so, от которого зависит исполняемый файл, он должен создать sh?

Это будет sh. Время cra sh зависит от того, как вы вызываете определенную экспортируемую функцию из файла .so. Вы можете получить все экспортированные функции через указатели на функции самостоятельно, используя dlopen dlysm и co. В этом случае программа выполнит обработку sh при первом вызове, если она не найдет экспортируемый метод.

В случае, если исполняемый файл просто вызывает экспортированный метод из общего объекта (часть его заголовка), Линкер Dynami c использует информацию о методе, который будет вызван в его исполняемом файле (см. второй ответ), и аварийно завершает работу в случае невозможности найти библиотеку или несоответствие символов.

Что на самом деле является Dynami c связывание, если все ссылки на внешние объекты разрешены при сборке? Это какая-то предварительная проверка, выполняемая динамическим компоновщиком c? Иначе, динамический c компоновщик может использовать LD_LIBRARY_PATH для получения дополнительных библиотек для разрешения неопределенных символов.

Вам необходимо различать фактическое связывание и соединение Dynami c. Начиная с фактического связывания: В случае связывания библиотеки stati c, фактическое связывание будет копировать весь код из метода, который будет вызываться внутри исполняемого файла / библиотеки, используя его. При связывании библиотеки Dynami c вы не будете копировать код, а только символы. Символы содержат смещения или другую информацию, указывающую на фактический код в библиотеке Dynami c. Если исполняемый файл вызывает метод, который не экспортируется динамической библиотекой c, он все равно потерпит неудачу в фактической части связывания.

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

1 голос
/ 24 января 2020

Разве динамическое связывание c не означает, что когда я запускаю исполняемый файл, используя ./executable_name, тогда, если компоновщик не может найти файл .so, от которого зависит исполняемый файл, он должен создать sh?

Да. Если файл .so отсутствует во время выполнения.

Что на самом деле означает динамическое связывание c, если все ссылки на внешние объекты разрешаются при сборке? Это какая-то предварительная проверка, выполняемая динамическим компоновщиком c? Еще динамический компоновщик c может использовать LD_LIBRARY_PATH для получения дополнительных библиотек для разрешения неопределенных символов.

Он позволяет обновлять библиотеки и позволяет приложениям по-прежнему использовать библиотеку, и он уменьшает использование памяти, загружая одну копию библиотеки вместо одной в каждом приложении, которое ее использует.

Компоновщик просто создает ссылки на эти символы, чтобы базовые переменные или функции можно было использовать позже. Он не связывает переменные и функции непосредственно в исполняемый файл.

Динамический компоновщик c не извлекает никакие библиотеки, если только эти библиотеки не указаны в исполняемом файле (или, как расширение любой библиотеки, от которой зависит исполняемый файл) , Если вы предоставляете каталог LD_LIBRARY_PATH с файлом .so совершенно другой версии, чем то, что требуется исполняемому файлу, исполняемый файл может создать sh.


В вашем случае кажется, что необходимое определение макроса не найдено, и компилятор использует правила неявного объявления. Вы можете легко исправить это, скомпилировав код с помощью -pedantic -pedantic-errors (при условии, что вы используете G CC).

...