Как внешние символы разделяемой библиотеки разрешаются в основной программе?

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

myShared.sh

int get(){
   return 0;
}

main.c

  extern int get();
  int main(){
    int a = get();
  }

Я понимаю, что, поскольку разделяемые библиотеки не могут делать никаких утверждений о том, где они будут размещены, они должны использовать GOT и PLT, чтобы делать ссылки на свои собственные функции и глобальные данные. Но как фактическая программа, использующая указанные библиотеки, узнает, куда будут загружены функции, чтобы она могла ссылаться на них? Очевидно, что компоновщик понятия не имеет, поскольку связывание таких библиотек происходит только во время загрузки. Таким образом, я могу думать только о двух способах, которые позволили бы ссылаться на такие внешние функции.

  1. Компоновщик просто помещает некоторые заполнители где get (в приведенном выше примере) вызывается, а затем добавляет некоторые метаданные, необходимые для загрузки, и затем заменяет заполнитель фактическим адресом функции (например, как совместно используемые библиотеки использовали перемещение во время загрузки перед PIC). вызвало ощутимые накладные расходы и было самой мотивацией (я думаю) для введения ПОС в первую очередь

  2. Основная программа также имеет свои собственные GOT и PLT, и загрузчик должен будет также заполнить GOT основной программы вместе с GOT общей библиотеки (либо все сразу во время загрузки в случае глобальных переменных, либо лениво, используя PLT для функций) Но это звучит как серьезное дублирование усилий.

Итак, какой из этих двух методов, если таковые имеются, используется для разрешения внешних символов разделяемых библиотек?

1 ответ

Решение

Разрешение символов в основной программе не сильно отличается от разрешения в разделяемых библиотеках и достигается с помощью таблиц GOT и PLT основной программы. Вы правы, что это вызывает определенное количество дублирования между статическими (ld) и динамический (ld.so) связывает и замедляет запуск программы, но с другой стороны обеспечивает большую гибкость во время выполнения (например, вставка символов через LD_PRELOAD).

Другие вопросы по тегам