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

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

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

Итак, в прошлой теме мы рассмотрели динамическую линковку с общей (shared) библиотекой (объектом). Чтобы уже добить эту тему, осталось рассмотреть вариант динамической загрузки библиотек.

Библиотеки Linux (по типу использования):

Изображение Шпаргалка по командам Linux, FreeBSD и MacOS

Динамическая загрузка библиотеки Си

Для реализации динамической загрузки библиотеки будем использовать интерфейс динамической загрузки (Dynamic Loading API). Имеется стандарт на реализацию для POSIX-совместимых систем, а также есть отдельные системо-специфичные возможности. Мы будем придерживать функций, описанных для POSIX.

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

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

Не будем изменять традиции, используем нашу библиотеку для работы со связным списком. Нам понядобятся dlopen() и dlsym() из <dlfcn.h>:

  • dlopen - Дает программе доступ к ELF-библиотеке;
  • dlsym - Возвращает адрес функции из библиотеки, загруженной при помощи dlopen;
  • dlerror - Возвращает текстовое описание последней возникшей ошибки;
  • dlclose - Закрывает доступ к библиотеке.
#include <dlfcn.h>
#include "linked_list.h"

// Помните, что функции - это указатели?
// Так вот, это как раз мы и используем ниже.
void (* print_list)(node_t *head);
void (* add)(node_t * head, char val);

int load_library() {
  // Открываем произвольный файл, реализующий интерфейс `linked_list.h`.
  // `RTLD_LAZY` - используем ленивый биндинг – подгружаем только символы
  // и соответствующие адреса. То есть берём адреса функций, но пока не
  // как функции.
  void * lib = dlopen("./liblinkedlist.so", RTLD_LAZY);
  if (lib == NULL) {
    return 1;
  }

  // Присваиваем адреса нужных символов нашим указателям (функциям)
  add = dlsym(lib, "add");
  print_list = dlsym(lib, "print_list");

  if (add == NULL) {
    return 2;
  }

  if (print_list == NULL) {
    return 3;
  }

  return 0;
}

int main() {
  int err = load_library();
  if (err) {
    printf("Library load error");
    return err;
  }

  // Подгрузили - пользуемся!
  char c;
  node_t head = { .next = NULL, .val = ' ' };

  while ((c = getc(stdin)) != EOF) {
    add(&head, c);
  }

  print_list(&head);
  return 0;
}
→ clang main.c
→ ./a.out

Таким образом мы можем реализовать подключаемые плагины для Си-шной программы. Можем подгрузить код, которого даже не было написано на момент запуска нашей программы!