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

UnixForum





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

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


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

3.7. Несовместимые изменения

Несовместимые изменения можно обрабатывать только с помощью механизма контроля версий символов, присутствующего в Linux и в GNU Hurd. Для Solaris придется вернуться к методу контроля версий с использованием имен файлов.

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

static int last;

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

int index1__ (int scale) {
	return next () < scale;
}
asm(".symver index1__,index@VERS_1.0");

int index2__ (int scale, int *result) {
	if (result < 0
		|| result >= 8 * sizeof (int))
	   return -1;
	*result = index1__ (scale);
	return 0;
}
asm(".symver index2__,index@@VERS_2.0");

Интерфейс index для версии VERS 2.0 реализован в index2 (примечание: это версия, используемая по умолчанию, поскольку в определении версии указано два символа @) совершенно по иному, и, как видно, поскольку в *result осуществляется запись, программы, которые ранее возвращали некоторое более или менее разумное значение, в настоящее время могут завершиться сбоем. Если функция будет использоваться со старым прототипом, то в этом параметре может оказаться мусор. Определение index1 точно такое же, как и в предыдущей реализации index. Т.к. имена index вводятся с помощью псевдооперации .symver, мы еще раз должны определить реальную функцию с алиасом.

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

Файл с таблицей символов для этого примера очень похож на один из вариантов для совместимых изменений:

VERS_1.0 {
	global: index;
	local: *;
};

VERS_2.0 {
	global: index;
}  VERS_1.0;

У нас есть два определения index, и, следовательно, это имя должно быть упомянуто в соответствующих разделах для двух версий.

Возможно, также стоило еще раз отметить, что вызов index1 в index2 не использует таблицу PLT, а вместо этого используется прямой переход, обычно относительно регистра PC.

С простым определением функции, таким как в этом примере, в случае, если в различных частях программы вызывают различные версии интерфейса index, вообще нет проблемы. Единственным требование, чтобы интерфейс, который был виден во время компиляции, также был тем интерфейсом, который компоновщик находил при обработке перемещаемого объектного файла. Поскольку в перемещаемом объектном файл нет информации о контроле версий, то нет возможности запомнить объектные файлы, находящиеся вокруг, и надеяться на то, что компоновщик выберет правильный интерфейс. Контроль версий символов работает только для объектов DSO и исполняемых файлов. Если позже необходимо повторно использовать перемещаемые объектные файлы, необходимо восстановить среду компоновки, которая была видна компоновщику при компилировании файла. Заголовочные файлы (для C, и любые другие спецификации интерфейса, имеющиеся в других языках) и объекты DSO, используемые при компоновке, образуют интерфейс API. Нельзя разделить два шага - компиляцию и компоновку. По этой причине системы сборки пакетов, в которых есть различие между пакетами времени выполнения и пакетами разработки, помещают заголовки и компонуемые объекты DSO в один файл, а файлы, необходимые во время выполнения программы, в другой.


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