В качестве примера мы пройдем
В качестве примера мы пройдем все шаги создания небольшой разделяемой библиотеки. В текст включены необходимые пояснения. Несколько надуманный текст программ примера важен не сам по себе, а как иллюстрация к возникающим проблемам.
Наша библиотека будет называться libexam. Пусть ее исходный текст первоначально представляет собой единственный файл, содержащий следующее:
/* первоначальный вид exam.c */
#include <stdio.h>
extern int strlen (); extern char *malloc, *strcpy ();
int count = 0; char *Error;
char *excopy (e) char *e; { char *new;
++count; if ((new=malloc (strlen (e)+1))==0) { Error="Нет памяти"; return 0; } return strcpy (new, e); }
excount () { fprintf (stderr, "сч. вып. %d\n", count); return count: }
Для начала выберем адреса библиотечных секций команд и данных (из адресов областей, выделенных для личного пользования). Заметим, что начальные адреса секций должны быть кратны 1 Мб:
.text 0x74000000 .data 0x74100000
Далее, определим маршрутное имя разделяемой библиотеки выполнения:
/my/directory/libexam_s
Теперь нужно определить внешние (импортируемые) имена библиотеки (рекомендации по этому поводу см. в разделе Использование импортируемых имен): malloc, strcpy, strlen, fprintf и _iob. Макросы для этих имен мы поместим во включаемый файл, причем обратите внимание, что на _iob нет прямой ссылки, это делается через макрос из <stdio.h>, определяющий stderr. Заметим также, что именам даны префиксы _libexam_. Указатели на импортируемые имена, будучи доступными извне, могут совпасть с какими-либо другими именами. Использование имени библиотеки в качестве префикса уменьшает вероятность такого конфликта.
/* новый файл import.h */
#define malloc (*_libexam_malloc) #define strcpy (*_libexam_strcpy) #define strlen (*_libexam_strlen) #define fprintf (*_libexam_fprintf) #define _iob (*_libexam__iob)
extern char *malloc (); extern char *strcpy (); extern int strlen (); extern int fprintf ();
Примечание
Объект _iob не описывается в файле import.h как внешний. Предполагается, что это делается во включаемом файле <stdio.h>.
Нам понадобится еще исходный файл, содержащий указатели на импортируемые имена. Помните, что все глобальные данные должны быть проинициализированы.
/* новый файл import.c */
#include <stdio.h>
char *(*_libexam_malloc) () = 0; char *(*_libexam_strcpy) () = 0; int (*_libexam_strlen) () = 0; int (*_libexam_fprintf) () = 0; FILE (*_libexam__iob) [] = 0;
Наконец, посмотрим, какие глобальные переменные библиотеки должны быть доступны использующим ее программам (см. ниже рекомендацию "уменьшать объем глобальных даннных"). К переменной count обращаются через excount(), поэтому она может не быть описана как внешняя. Опишем ее как static (это уже должно было быть сделано и для архивной перемещаемой библиотеки).
Глобальные данные библиотеки необходимо переместить в отдельные исходные файлы (см. ниже рекомендацию "хранить функции и глобальные данные в различных исходных файлах"). Осталась единственная глобальная переменная Error, и ее нужно проинициализировать (см. ниже рекомендацию "инициализировать глобальные переменные"). Error должна остаться глобальной, поскольку через нее передается информация в вызывающую программу.
/* новый файл global.c */
char *Error = 0;
В связи с перечисленными изменениями исходный файл должен теперь выглядеть так (обратите внимание, что имена функций необходимо описать как внешние):
/* измененный exam.c */
#include "import.h"
#include <stdio.h>
extern int strlen (); extern char *malloc, *strcpy ();
static int count = 0; extern char *Error;
char *excopy (e) char *e; { char *new;
++count; if ((new=malloc (strlen (e)+1))==0) { Error="Нет памяти"; return 0; } return strcpy (new, e); }
excount () { fprintf (stderr, "сч. вып. %d\n", count); return count: }
Примечание
Файл import.h должен включаться ранее файла <stdio.h>.
Теперь нужно ввести файл спецификаций разделяемой библиотеки для последующего выполнения mkshlib(1):
/* новый файл libexam.sl */
1 #target /my/directory/libexam_s 2 #address .text 0x74000000 3 #address .data 0x74100000
4 #branch 5 excopy 1 6 excount 2
7 #objects 8 import.o 9 global.o 10 exam.o
11 #init import. o 12 _libexam_malloc malloc 13 _libexam_strcpy strcpy 14 _libexam_strlen strlen 15 _libexam_fprintf fprintf 16 _libexam__iob _iob
(Номера строк включены для удобства ссылок; они не являются частью примера.) Вкратце, в этом файле спецификаций указано следующее. В строке 1 задается маршрутное имя разделяемой библиотеки выполнения. В дальнейшем библиотека должна быть помещена в этот файл, чтобы использующие ее программы могли выпол- няться. Виртуальные адреса для секций команд и данных библиотеки задаются в строках 2 и 3 соответственно. В строках 4-6 описывается таблица. В строках 5 и 6 функциям excopy() и excount()
назначаются элементы 1 и 2 таблицы переходов. Напомним, что ее элементы должны назначаться только именам внешних функций.
В строках 7-10 перечисляются объектные файлы, из которых и будут собраны разделяемые библиотеки сборки и выполнения. В разделяемой библиотеке сборки каждый из них будет оформлен как отдельный элемент. В разделяемой библиотеке выполнения указанный порядок следования этих файлов сохранится. Первыми нужно указывать файлы данных, иначе, например, изменение размера статических переменных в exam.o привело бы к изменению адресов внешних переменных, после чего ранее созданные программы не смогли бы больше работать с этой библиотекой.
В строках 11-16 находятся данные о внешних именах файла import.o. Их можно понимать как своего рода операторы присваивания, например, _libexam_malloc будет содержать адрес malloc и т.д.
Далее, как и для архивной библиотеки, нужно создать объектные файлы:
cc -c import.c global.c exam.c
Осталось запустить mkshlib(1) для создания разделяемой библиотеки сборки и разделяемой библиотеки выполнения:
mkshlib -s libexam.sl -t libexam_s -h libexam_s.a
Если компиляция исходных текстов прошла нормально, mkshlib(1) создаст разделяемую библиотеку сборки libexam_s.a и разделяемую библиотеку выполнения libexam_s.