Программы
Сделаем свою небольшую общую библиотеку

Сделаем свою небольшую общую библиотеку

Мы уже делали библиотеку для Си. Настало сделать общую библиотеку!

В предыдущей теме мы написали свою небольшую "библиотеку" для работы со связным списком. Также рассмотрели вариант линковки прямо в проект объектным файлом – так называемая "статическая линковка". Однако, многие библиотеки поставляются не в виде .c и .h файлов, а в виде .so файлов, а также .h файлов, чтобы их использовать. Мы видели .o – object файлы, теперь – .so – shared object.

Всё тот же linked_list.h

#ifndef	_LINKED_LIST_H_
#define	_LINKED_LIST_H_

#include <stdio.h>

typedef struct node {
  struct node * next;
  char val;
} node_t;

void print_list(node_t *head);
void add(node_t * head, char val);

#endif /* _LINKED_LIST_H_ */

Ну и сам linked_list.c:

#include "linked_list.h"
#include <stdlib.h>

void print_list(node_t *head) {
  node_t * current = head;

  while (current != NULL) {
    printf("%c->", current->val);
    current = current->next;
  }

  printf("\n");
}

void add(node_t * head, char val) {
  node_t * current = head;
  while (current->next != NULL) {
    current = current->next;
  }

  current->next = (node_t * )malloc(sizeof(node_t));
  current->next->val = val;
  current->next->next = NULL;
}

main.c нам пока не нужен. Наша задача – сделать возможным использование нашего кода не одной конкретной программой, а всей системой.

Собираем:

→ clang --shared -fPIC linked_list.c -o liblinkedlist.so

– указываем, что создаём shared object, в котором будет использовано относительное позиционирование кода (Position Independent Code).

PIC позволяет машинному коду адресоваться без необходимости использовать абсолютные адреса виртуальной памяти. Это довольно удобно, когда код загружается в память и может быть использован различными независимыми процессами. Именно для этого мы и делаем shared object.

Используем свежий shared object:

→ clang main.c -fPIC -L. -llinkedlist -o linked_list

– из нового только ключ -L – чтобы указать путь, по которому мы будем подключать дополнительные библиотеки.

Всё хорошо собралось и даже работает. Теперь переименуем, чтобы проверить, что при следующем запуске ./linked_list библиотека будет не найдена:

→ mv liblinkedlist.so liblinkedlist.so.1
→ ./linked_list
dyld: Library not loaded: liblinkedlist.so
 Referenced from: /$PWD/./linked_list
 Reason: image not found
Abort trap: 6

– dyld MacOS не может найти.

Что внутри?

Можно посмотреть по бинарному файлу, что же он использует. В Linux утилита ldd, в MacOS - otool -L

→ otool -L linked_list
linked_list:
	liblinkedlist.so (compatibility version 0.0.0, current version 0.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.0.0)

Также можно посмотреть "имена", определённые в shared object:

→ nm liblinkedlist.so
0000000000000f10 T _add
                 U _malloc
0000000000000eb0 T _print_list
                 U _printf
                 U dyld_stub_binder

Как это работает?

В Linux за динамическую линковку отвечает подсистема ld. Используются

  • дефолтные пути (/usr/lib, /usr/local/lib)
  • $LD_LIBRARY_PATH, ключ -L для указания, где искать
  • $LD_PRELOAD

В случае использования динамической компоновки ядро передает управление на динамический компоновщик (другое название – ELF-интерпретатор), который после собственной инициализации загружает указанные совместно используемые библиотеки (если они уже не в памяти). Далее динамический компоновщик производит необходимые перемещения (relocations), включая совместно используемые объекты, на которые ссылаются требуемые совместно используемые библиотеки. Путь, по которому система будет искать совместно используемые объекты, задается переменной среды LD_LIBRARY_PATH. Закончив с библиотеками, компоновщик отдает управление исходной программе, которая начинает выполнение.

В основе процесса перемещения (relocation) лежит косвенная адресация, которую обеспечивают две таблицы – глобальная таблица смещений (Global Offset Table, GOT) и таблица связывания процедур (Procedure Linkage Table, PLT). В этих таблицах содержатся адреса внешних функций и данных, которые ld-linux.so должен загрузить в процессе перемещения. Получается, что код, содержащий обращение к внешним функциям и, таким образом, ссылающийся на данные этих таблиц, остается неизменным – модифицировать требуется только таблицы. Перемещение может проходить либо сразу во время загрузки программы, либо когда понадобится нужная функция.

По завершении перемещения динамический компоновщик исполняет стартовый код каждой совместно используемой библиотеки (если этот код имеется), содержащий инициализацию и подготовку внутренних данных. Стартовый код определяется в секции .init ELF-файла. Во время выгрузки библиотеки может выполняться также и завершающий код, определяемый в секции .fini. Вызвав функции инициализации, динамический компоновщик отдает управление исходному исполняемому образу.

Также может быть вам интересно:

Как добавить самоподписанный сертификат в MacOS

Читать »

"Линковка" во время работы программы

Доведём тему линковки до конца – когда для сборки и запуска программы нам даже не нужна сама библиотека!

Читать »
Фото Как установить PostgreSQL на Linux и создать базу и пользователя

Как установить PostgreSQL на Linux и создать базу и пользователя

PostgreSQL - система управления базой данных общего назначения. Одна из самых распространённых баз данных, используемая на многих коммерческих и некоммерческих проектах.

Фото Как сделать свою middleware в Django (с примерами)

Как сделать свою middleware в Django (с примерами)

Middleware или "промежуточное программное обеспечение" - элегантный способ установить общие правила обработки запросов и ответов приложения. Давайте напишем парочку middleware, чтобы понять, как они работают.

Фото Как настроить отправку почты из Django

Как настроить отправку почты из Django

Письма об ошибках, отчёты на почту, восстановление паролей - всё это полезно при работе с сайтом. Django предоставляет удобный способ это сделать с минимумом настроек!

Фото Добавляем поддержку медиа-файлов в Django проект

Добавляем поддержку медиа-файлов в Django проект

Современные сайты редко ограничиваются только текстом и вёрсткой. Часто в заметках красуются фотографии, а рядом с описанием товаров - их изображения.

Фото Добавляем постраничную пагинацию на Django сайт

Добавляем постраничную пагинацию на Django сайт

На сайтах часто встречаются многостраничные объекты: список товаров, список заметок и т.д. Поэтому важно уметь добавить навигацию по страницам на Django-проекте.

Фото Новый оператор match-case в Python

Новый оператор match-case в Python

В новой версии Python (3.10) появится новый оператор. Новый оператор сопоставления по шаблону (match-case).

Фото Нет слов, одни... однострочники

Нет слов, одни... однострочники

На днях вышел пост со списком полезных однострочников для JavaScript программистов. Памятуя Perl-овую молодость, заглянул туда.

Фото Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

В Django вы можете передавать данные в шаблоны посредством контекстов. Контекст передаётся из контроллера (view в терминах Django), однако, если одни и те же данные нужны в разных местах, лучше сделать свой контекст-процессор.