Как динамический компоновщик узнает, в какой библиотеке искать символ? - PullRequest
3 голосов
/ 20 сентября 2019

Я экспериментирую с LD_PRELOAD / dlopen и столкнулся с путаницей в отношении поиска символов.Рассмотрим следующие 2 библиотеки:

  1. libshar

shared.h

int sum(int a, int b);

shared.c

int sum(int a, int b){
    return a + b;
}
libshar2

shared.h

int sum(int a, int b);

shared.c

int sum(int a, int b){
    return a + b + 10000;
}

и исполняемый файл bin_shared:

#include <dlfcn.h>
#include "shared.h"

int main(void){
    void *handle = dlopen("/home/me/c/build/libshar2.so", RTLD_NOW | RTLD_GLOBAL);
    int s = sum(2 + 3);
    printf("s = %d", s);
}

связывание двоичного файла с libshar и libdl Я рассмотрел следующие 2 случая:

  1. LD_PRELOAD пусто

Программа печатает 5.

Почему динамический компоновщик решает искать функцию sum в libshar, а не libshar2?Они оба загружены и содержат необходимый символ:

0x7ffff73dc000     0x7ffff73dd000     0x1000        0x0 /home/me/c/build/libshar2.so
0x7ffff73dd000     0x7ffff75dc000   0x1ff000     0x1000 /home/me/c/build/libshar2.so
0x7ffff75dc000     0x7ffff75dd000     0x1000        0x0 /home/me/c/build/libshar2.so
0x7ffff75dd000     0x7ffff75de000     0x1000     0x1000 /home/me/c/build/libshar2.so
#...
0x7ffff7bd3000     0x7ffff7bd4000     0x1000        0x0 /home/me/c/build/libshar.so
0x7ffff7bd4000     0x7ffff7dd3000   0x1ff000     0x1000 /home/me/c/build/libshar.so
0x7ffff7dd3000     0x7ffff7dd4000     0x1000        0x0 /home/me/c/build/libshar.so
0x7ffff7dd4000     0x7ffff7dd5000     0x1000     0x1000 /home/me/c/build/libshar.so
LD_PRELOAD = /path/to/libshar2.so

Программа печатает 10005.Это ожидаемо, но я снова заметил, что загружены и libshar.so, и libshar2.so:

0x7ffff79d1000     0x7ffff79d2000     0x1000        0x0 /home/me/c/build/libshar.so
0x7ffff79d2000     0x7ffff7bd1000   0x1ff000     0x1000 /home/me/c/build/libshar.so
0x7ffff7bd1000     0x7ffff7bd2000     0x1000        0x0 /home/me/c/build/libshar.so
0x7ffff7bd2000     0x7ffff7bd3000     0x1000     0x1000 /home/me/c/build/libshar.so
0x7ffff7bd3000     0x7ffff7bd4000     0x1000        0x0 /home/me/c/build/libshar2.so
0x7ffff7bd4000     0x7ffff7dd3000   0x1ff000     0x1000 /home/me/c/build/libshar2.so
0x7ffff7dd3000     0x7ffff7dd4000     0x1000        0x0 /home/me/c/build/libshar2.so
0x7ffff7dd4000     0x7ffff7dd5000     0x1000     0x1000 /home/me/c/build/libshar2.so

Случай LD_PRELOAD, кажется, объясняется в ld.so(8):

LD_PRELOAD

Список дополнительных пользовательских ELF-объектов, указанных пользователем, которые должны быть загружены раньше всех остальных.Элементы списка могут быть разделены пробелами или двоеточиями. Это можно использовать для выборочного переопределения функций в других общих объектах .Для поиска объектов используются правила, приведенные в разделе ОПИСАНИЕ.

Ответы [ 2 ]

3 голосов
/ 20 сентября 2019

Почему динамический компоновщик решает искать функцию суммы в libshar, а не в libshar2?

Динамические компоновщики в UNIX пытаются эмулировать то, что произошло бы , если вы связались с архивом библиотеками.

В случае пустогоLD_PRELOAD, поиск символа порядок (когда на символ ссылается основной двоичный файл; правила усложняются, когда на символ ссылается DSO): основной двоичный файл, непосредственно связанные DSO в порядкеони перечислены в строке ссылки, dlopen ed DSO в том порядке, в котором они были dlopen ed.

LD_PRELOAD = /path/to/libshar2.so Программа печатает 10005. Ожидается, что

Непустой LD_PRELOAD изменяет порядок поиска, вставляя все библиотеки, перечисленные после основного исполняемого файла, и перед любыми напрямую связанными DSO.

, но я снова заметил, что и libshar.so, и libshar2.so загружены:

Почему это сюрприз?Динамический компоновщик загружает все библиотеки, перечисленные в LD_PRELOAD, а затем все библиотеки, с которыми вы непосредственно связаны (как объяснено ранее).

2 голосов
/ 20 сентября 2019

dlopen не может (и не может ничего другого) изменить определение (глобальных) символов, уже присутствовавших на момент вызова.Он может делать доступными только новые, которых раньше не было.

(небрежное) оформление этого в спецификации для dlopen:

Символы, введенные в образ процесса через вызовы dlopen (), могут использоваться в действиях по перемещению.Введенные символы могут дублировать символы, уже определенные программой или предыдущими операциями dlopen ().Чтобы разрешить неоднозначности, которые может возникнуть в такой ситуации, разрешение ссылки на символ для определения символа основано на порядке разрешения символов.Определены два таких порядка разрешения: порядок загрузки и порядок зависимости.Порядок загрузки устанавливает порядок среди определений символов, так что первое загруженное определение (включая определения из файла образа процесса и любые зависимые исполняемые объектные файлы, загруженные с ним) имеет приоритет над исполняемыми объектными файлами, добавленными позже (с помощью dlopen ()).Упорядочение нагрузки используется при обработке перемещения.Порядок зависимостей использует порядок в ширину, начиная с заданного исполняемого объектного файла, затем всех его зависимостей, затем любых их зависимостей, итерируя, пока все зависимости не будут удовлетворены.За исключением дескриптора глобальной таблицы символов, полученного с помощью операции dlopen () с нулевым указателем в качестве аргумента файла, упорядочение зависимостей используется функцией dlsym ().Упорядочение нагрузки используется в операциях dlsym () над дескриптором таблицы глобальных символов.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...