10.6.1 Реализация совместно используемых библиотек в uClinux (libN.so)

Предыдущая  Содержание  Следующая V*D*V

Как мы уже видели, uClinux использует для исполняемых файлов формат bFLT. Загрузчик ядра uClinux выполняет работу по загрузке приложения в доступное место в памяти, настраивая необходимые разделы данных и кода. Напомним также, что если загружаемый файл bFLT имеет возможность выполнения на месте (XIP), то загрузчик ядра будет использовать один и тот же текстовый сегмент для несколько экземпляров приложения, а также создаст отдельные разделы данных для каждого экземпляра. Эта поддержка XIP используется в качестве базы для реализации в uClinux общих библиотек. Если текст не выполняем на месте, то общие библиотеки не могут работать. Это происходит потому, что общие текстовые страницы не могут существовать в памяти без поддержки MMU. Поэтому идея состоит в том, чтобы хранить их в общедоступном месте (флеш-памяти), и чтобы все программы обращались непосредственно к одному месту.

Для uClinux существует несколько реализаций различных типов совместно используемых библиотек. Мы обсудим реализацию динамических библиотек для m68k, которая использует файлы bFLT, известный в народе как метод libN.so. В этом методе общие библиотеки представляют собой бинарными файлы bFLT, но такие, которые содержат внутри определённый идентификатор библиотеки (ID). Это требует изменений в компиляторе, а также загрузчике. Каждый символ, на который есть ссылка в исполняемом модуле, имеет добавленный к нему определённый идентификатор библиотеки. Когда загрузчик должен обработать символ, он ищет ссылку с помощью ID, содержащегося в символе, и таким образом определяет необходимую библиотеку. Для упрощения поиска имена совместно используемых библиотек следуют определённому шаблону. Библиотека, которая имеет ID=X, имеет имя libX.so. Так что когда загрузчик должен обработать символ с ID=X в нём, он просто загружает файл /lib/libX.so.

Компилятор создаёт отдельный сегмент GOT и данных для приложения и для каждой библиотеки. Когда программа загружается, он обеспечивает, чтобы отдельные сегменты данных (в разделяемых библиотеках) были доступными с фиксированными смещениями от базового адреса. Уникальный идентификационный номер, выделенный для каждой библиотеки, используется для определения смещения, чтобы найти определённый сегмент данных. Приложение также должно использовать один и тот же метод обращений с использованием идентификатора и значение идентификатора 0 зарезервировано для приложения.

Поддержку совместно используемой библиотеки в ядре осуществляет компонент, выбираемый с помощью флага CONFIG_BINFMT_SHARED_FLAT. Основной структурой для поддержки разделяемой библиотеки в uClinux является  показанная ниже структура lib_info.

 

struct lib_info {

  struct {

    unsigned long start_code; /* Начало текстового сегмента */

    unsigned long start_data; /* Начало сегмента данных */

    unsigned long start_brk;  /* Конец сегмента данных */

    unsigned long text_len;   /* Длина текстового сегмента */

    unsigned long entry;      /* Начальный адрес для этого

                                 модуля */

    unsigned long build_date; /* Когда он был скомпилирован */

    short loaded;             /* Загружена ли эта библиотека? */

  } lib_list[MAX_SHARED_LIBS];

};

 

Макрос MAX_SHARED_LIBS определяется как 4, если CONFIG_BINFMT_SHARED_FLAT установлен, либо по умолчанию используется значение 1. Эта структура используется для хранения списка библиотек, загруженных для каждого приложения. Хотя теоретический предел максимального числа библиотек составляет 255 - 1, ядро определяет его как 4, что позволяет использовать 3 разделяемые библиотеки для каждого приложения. Значение ID равное 0 используется как относящееся к загружаемому приложению. Распознавание символа приложения, использующего разделяемую библиотеку, происходит внутри функции calc_reloc, показанной в Распечатке 10.5.

Обратите внимание на извлечение значения id, в которое будет переведено r, использующее id = (r >> 24) & 0xff. Изменения в компоновщике позволяют размещать значение id в старшем байте адреса символа. Например, если символ foo не определён в приложении, но определён в библиотеке с ID=3, то такой символ в данном приложении будет иметь запись форме 0x03XX_XXXX = (0x0300_0000 | адрес foo() в lib3.so). Таким образом, все внешние по отношению к приложению символы в своём старшем байте будут иметь id, соответствующий библиотеке.

Фактическая загрузка библиотеки в программную память происходит внутри функции load_flat_shared_library. Эта функция загружает файл /lib/libX.so используя обычную функции загрузчика файла типа "flat" load_flat_file, передавая соответствующее значение id=X. (* Обратите внимание, что во время выполнения двоичных файлов типа "flat", exec() использует эту же функцию с id=0.) Загрузчик двоичного файла "flat" поддерживает массив lib_info.lib_list[], чтобы отслеживать все загружаемые файлы, включая приложение, которое загружается по адресу 0. Загрузчик обеспечивает, чтобы были доступны все неопознанные/внешние символы, необходимые для исполнения.

Вышеописанная реализация является одной наиболее широко используемой для процессоров на основе m68k и была предложена Полом Дейлом. Для uClinux доступны и другие реализации совместно используемой библиотеки. XFLAT от Cadenux представляет собой другую реализацию совместно используемой библиотеки, широко используемую на процессорах ARM. FRV-uClinux также имеет реализацию разделяемой библиотеки. Каждая реализация было сделано с разными целями, решает разные проблемы.

 

Предыдущая  Содержание  Следующая