C dll for linux

Создание и использование динамических библиотек, написанных на различных языках (C/C++, Pascal)

Передо мной возникла задача написать загрузчик библиотек, имеющий возможность предоставить какие-то интерфейсные функции внешней динамической библиотеке. Решение должно быть максимально кроссплатформенно (как минимум, работать на Linux и Windows). Загружаться должны библиотеки, написанные на различных языках программирования, поддерживающих создание динамических библиотек. В качестве примера были выбраны языки C и Pascal.

Решение

Основной загрузчик библиотек написан на языке C. Для того, чтобы загружаемые библиотеки имели возможность использовать функции основной программы, основная программа разделена на 2 части: на основной и подгружаемый модули. Основной модуль нужен просто для запуска программы, подгружаемый модуль — это также динамическая библиотека, связываемая с основным модулем во время его запуска. В качестве компиляторов были выбраны gcc (MinGW для Windows) и fpc.
Здесь будет приведён упрощённый пример программы, позволяющий разобраться в данном вопросе и учить первокурсников писать модули к своей программе (в школе часто преподают именно Pascal).

Загрузчик библиотек
main.c
#include "loader.h" #ifdef __cplusplus extern "C" < #endif int main(int argc, char *argv[]) < if (argc >1) < loadRun(argv[1]); >return 0; > #ifdef __cplusplus > #endif 

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

loader.c
#include "loader.h" #include "functions.h" #include #ifndef WIN32 #include #else #include #endif #ifdef __cplusplus extern "C" < #endif void printString(const char * const s) < printf("String from library: %s\n", s); >void loadRun(const char * const s) < void * lib; void (*fun)(void); #ifndef WIN32 lib = dlopen(s, RTLD_LAZY); #else lib = LoadLibrary(s); #endif if (!lib) < printf("cannot open library '%s'\n", s); return; >#ifndef WIN32 fun = (void (*)(void))dlsym(lib, "run"); #else fun = (void (*)(void))GetProcAddress((HINSTANCE)lib, "run"); #endif if (fun == NULL) < printf("cannot load function run\n"); >else < fun(); >#ifndef WIN32 dlclose(lib); #else FreeLibrary((HINSTANCE)lib); #endif > #ifdef __cplusplus > #endif 
Заголовочные файлы

Это всё была реализация, а теперь заголовочные файлы.
Вот интерфейс модуля, загружающего динамические библиотеки, для основного модуля:

loader.h
#ifndef LOADER_H #define LOADER_H #ifdef __cplusplus extern "C" < #endif extern void loadRun(const char * const s); #ifdef __cplusplus >#endif #endif 

Вот интерфейс загрузчика для загружаемых им динамических библиотек (перечень функций, которые динамические библиотеки могут использовать в основной программе):

Читайте также:  Linux исполняемые файлы elf
functions.h
#ifndef FUNCTIONS_H #define FUNCTIONS_H #ifdef __cplusplus extern "C" < #endif extern void printString(const char * const s); #ifdef __cplusplus >#endif #endif 

Как видно, здесь всего одна функция printString для примера.

Компиляция загрузчика

Пример недистрибутивной компиляции (в случае Windows в опции компилятора просто нужно добавить -DWIN32):

$ gcc -Wall -c main.c $ gcc -Wall -fPIC -c loader.c $ gcc -shared -o libloader.so loader.o -ldl $ gcc main.o -ldl -o run -L. -lloader -Wl,-rpath,. 

Дистрибутивная компиляция от недистрибутивной отличается тем, что в дистрибутивном случае динамические библиотеки ищутся в /usr/lib и имеют вид lib$(NAME).so.$(VERSION), в случае недистрибутивной компиляции они называются lib$(NAME).so, а ищутся в каталоге запуска программы.

Теперь посмотрим, что у нас получилось после компиляции:

$ nm run | tail -n 2 U loadRun 08048504 T main $ nm libloader.so| tail -n 4 000005da T loadRun 000005ac T printString U printf@@GLIBC_2.0 U puts@@GLIBC_2.0 

Тут видим, что функции, отмечаемые как U ищутся во внешних динамических библиотеках, а функции, отмечаемые как T предоставляются модулем. Это бинарный интерфейс программы (ABI).

Динамические библиотеки

Библиотека на языке C
lib.c
#include "functions.h" #ifdef __cplusplus extern "C" < #endif void run(void) < printString("Hello, world!"); >#ifdef __cplusplus > #endif 

Здесь везде окружение extern «C» <> нужно для того, чтобы нашу программу можно было компилировать при помощи C++-компилятора, такого, как g++. Просто в C++ можно объявлять функции с одним и тем же именем, но с разной сигнатурой, соответственно в этом случае используется так называемая декорация имён функций, то есть сигнатура функций записывается в ABI. Окружение extern «C» <> нужно для того, чтобы не использовалась эта декорация (тем более, что эта декорация зависит от используемого компилятора).

Компиляция
$ gcc -Wall -fPIC -c lib.c $ gcc -shared -o lib.so lib.o 
$ nm lib.so | tail -n 2 U printString 0000043c T run 
$ ./run lib.so String from library: Hello, world! 

Если мы уберём в нашем модуле окружение extern «C» <> и скомпилируем при помощи g++ вместо gcc, то увидим следующее:

$ nm lib.so | grep run 0000045c T _Z3runv 

То есть, как и ожидалось, ABI библиотеки изменился, теперь наш загрузчик не сможет увидеть функцию run в этой библиотеке:

$ ./run lib.so cannot load function run 
Библиотека на языке Pascal

Как мы увидели выше, для того, чтобы наш загрузчик видел функции в динамических библиотеках, созданных компилятором C++, пришлось дополнять наш код вставками extern «C» <>, это притом, что C/C++-компиляторы и языки родственные. Что уж говорить про компилятор FreePascal совершенно другого языка — Pascal? Естественно, что и тут без дополнительных телодвижений не обойтись.

Читайте также:  Linux установка dhcp server

Для начала нам нужно научиться использовать экспортированные в C функции для динамических библиотек. Вот пример аналогичного C/C++ заголовочного файла на языке Pascal:

func.pas
unit func; interface procedure printString(const s:string); stdcall; external name 'printString'; implementation end. 

Вот пример самого модуля на языке Pascal:

modul.pas
library modul; uses func; procedure run; stdcall; begin printString('Hello from module!'); end; exports run; begin end. 
Компиляция
$ fpc -Cg modul.pas Компилятор Free Pascal версии 2.5.1 [2011/02/21] для i386 Copyright (c) 1993-2010 by Florian Klaempfl Целевая ОС: Linux for i386 Компиляция modul.pas Компоновка libmodul.so /usr/bin/ld: warning: link.res contains output sections; did you forget -T? /usr/bin/ld: warning: creating a DT_TEXTREL in a shared object. 13 строк скомпилиpовано, 6.6 сек 
$ nm libmodul.so U printString 000050c0 T run 

Как видим, ничего лишнего, однако настораживает предупреждение ld во время компиляции. Находим в гугле возможную причину предупреждения, это связано с компиляцией без PIC (Position Independent Code — код не привязан к физическому адресу), однако в man fpc находим, что наша опция -Cg должна генерировать PIC-код, что само по себе странно, видимо fpc не выполняет своих обещаний, либо я делаю что-то не так.

Теперь попробуем убрать в нашем заголовочном файле кусок name ‘printString’ и посмотрим, что выдаст компилятор теперь:

$ nm libmodul.so U FUNC_PRINTSTRING$SHORTSTRING 000050d0 T run 

Как видно, декорация в FreePascal совсем другого рода, чем в g++ и тоже присутствует.
При запуске с этим модулем получаем:

$ ./run libmodul.so ./run: symbol lookup error: ./libmodul.so: undefined symbol: FUNC_PRINTSTRING$SHORTSTRING 
$ ./run libmodul.so String from library: Hello from module! 

Вот и всё, своей задачи — использование динамических библиотек, написанных на различных языках — мы достигли, и наш код работает как под Linux, так и под Windows (у самого Mac’а нет, поэтому не смотрел, как там с этим). Кроме того, загруженные библиотеки имеют возможность использовать предоставляемые в основной программе функции для взаимодействия с ней (то есть являются плагинами основной программы).

Сам загрузчик может выполняться в отдельном процессе, чтобы эти плагины не смогли сделать ничего лишнего с основной программой. Соответственно, основная программа может быть написана на любом другом удобном языке программирования (например, на Java или на том же C++).

Читайте также:  Отключить ждущий режим линукс

Литература

Источник

Static Library

A static library is basically an archive (like a zip file) of object files, which are compiled from the .c/.cpp source code.

(The -c switch means: Compile and assemble, but do not link.)

Thus, we get two files test1.o and test2.o.

Now, we can use ar to put these object files together into a single static library.

ar rsv testlib.a test1.o test2.o

Now the testlib.a contains test1.o and test2.o.

gcc -o test.out test.c testlib.a

Alternatively, you could use the explicity linking options to link the static library (-L switch specifies the static library path and -l followed by the name of the static library):

gcc -o test.out test.c -L. -ltestlib

The static library is distributed with a function declaration header files .h, so that you know how to invoke them and the compiler takes care of them e.g. linking .a static libraries into your executables.

The Dynamic Link Library (DLL) is stored separately from the target application and shared among different applications, compared to Static Library. The DLL is the file extension on Windows while on Linux, it is *.so (Shared Object).

The .so/.dll can be loaded right before the application starts or during the application’s runtime. On Windows, the Win32 API LoadLibrary is used while on Linux gcc compiler, the dlopen function is used.

gcc -shared -o libhello.so -fPIC hello.c
g++ Application.cpp -o Application -I/home/wjw/dev/Youtube/OpenGL/Dependencies/GLFW/include -L/home/wjw/dev/Youtube/OpenGL/Dependencies/GLFW/lib -lglfw
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:~/dev/Youtube/OpenGL/Dependencies/GLFW/lib

Calling C++ Shared Library from Python Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// https://helloacm.com
extern «C»
{
// A function adding two integers and returning the result
int SampleAddInt(int i1, int i2)
{
return i1 + i2;
}

// A function doing nothing 😉
void SampleFunction1()
{
// insert code here
}

// A function always returning one
int SampleFunction2()
{
// insert code here

return 1;
}
}
g++ -Wall -O3 -shared TestLib.c -o TestLib.so
#!/usr/bin/python

import ctypes

def main():
TestLib = ctypes.cdll.LoadLibrary(‘/home/wjw/misc/cpp_static_dynamic_lib/TestLib.so’)
print(TestLib.SampleAddInt(1, 2))

if __name__ == ‘__main__’:
main()

Источник

Оцените статью
Adblock
detector