Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

На главную -> MyLDP -> Программирование и алгоритмические языки


Ulrich Drepper "Как писать разделяемые библиотеки"
Назад Оглавление Вперед

2.2 Управление экспортом

Когда из коллекции файлов с объектами создается объект DSO, в таблице динамических символов будут по умолчанию находиться все символы, видимость которых в файлах объектов глобальная. В большинстве случаев этот набор слишком большой. Экспортироваться должны только те символы, которые фактически являются частью интерфейса ABI. Неспособность ограничить набор экспортируемых символов имеет множество недостатков:

  • Пользователи объекта DSO могут использовать интерфейсы, о которых не подозревалось. Это вызовет проблемы при ревизиях DSO, которые связаны с двоичной совместимостью. Как правильно предполагает разработчик DSO, интерфейсы, которые не являются частью интерфейса ABI, могут быть произвольным образом изменены. Но всегда есть пользователи, которые утверждает, что знают лучше или не заботятся о правилах.
  • Согласно правилам поиска ELF, для всех символов в динамической таблице символов допускаются подстановки (если только видимость символа не ограничена). То есть можно использовать определения из других объектов. Это означает, что во время компоновки нельзя связывать локальные ссылки. Если известно или предполагается, что всегда должно использоваться локальное определение, символ из ссылки не должен экспортироваться или его видимость должна быть ограничена.
  • Динамическая таблица символов и ее таблица строк доступны во время выполнения программы и, следовательно, они должны быть загружены. Для этого может потребоваться значительное количество памяти, даже хотя она используется только для чтения. Можно подумать, что объем памяти не является большой проблемой, а, но если проверить длину трансформированных имен переменных или функций в С++, то становится очевидным, что это не так. В добавок у нас есть затраты времени выполнения для больших таблиц символов, которые мы уже обсуждали в предыдущем разделе.

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

При рассмотрении различных методов мы будем пользоваться следующим примером:

int last;

int next (void)  {
   return ++last;
}

int index (int scale) {
   return next () < scale;
}

Объект DSO, содержащий только этот код (плюс код запуска и т.д.) и скомпилированный на машине IA-32 Linux, имеет семь перемещений, два из которых являются относительными, а также четыре записи PLT (используется скрипт relinfo). Мы увидим, как мы можем улучшить эту ситуацию. Четыре обычных и оба относительных перемещения, а также три записи PLT записи создаются дополнительным кодом, используемым компоновщиком для создания объекта DSO. В реальномпримере кода создается только одно обычное перемещение для last и одна запись PLT для next. Для того, чтобы в next увеличить на единицу и прочитать значение переменной last, компилятор сгенерирует код, например, следующий

movl   last@GOT(%ebx), %edx
movl   (%edx), %eax
incl    %eax
movl   %eax, (%edx)

и вызов next компилируется как

call next@PLT

Эти фрагменты кода, были описаны в разделе 1.5.5.


Предыдущий раздел:   Следующий раздел:
Назад Оглавление Вперед