🔝 Retour au Sommaire
Un Shared Object (objet partagé) est le format de bibliothèque partagée utilisé par Linux et les systèmes Unix. C'est l'équivalent Linux des DLL Windows. Ces fichiers portent généralement l'extension .so et suivent des conventions de nommage spécifiques.
Sur Linux, tout repose sur les bibliothèques partagées :
- La bibliothèque C standard :
libc.so.6 - Les bibliothèques graphiques :
libX11.so,libgtk-3.so - Les pilotes :
libGL.so(OpenGL) - Et des milliers d'autres...
Cette approche favorise :
- La modularité du système
- Les mises à jour de sécurité centralisées
- L'économie de ressources
Les .so utilisent le format ELF, standard sur Linux :
┌─────────────────────────────────────┐
│ En-tête ELF (ELF Header) │ ← Métadonnées du fichier
├─────────────────────────────────────┤
│ Table des en-têtes de programme │ ← Segments pour l'exécution
├─────────────────────────────────────┤
│ Table des symboles (.symtab) │ ← Symboles de débogage
├─────────────────────────────────────┤
│ Table des symboles dynamiques │ ← Symboles exportés
│ (.dynsym) │
├─────────────────────────────────────┤
│ Section .text │ ← Code exécutable
├─────────────────────────────────────┤
│ Section .data │ ← Données initialisées
├─────────────────────────────────────┤
│ Section .bss │ ← Données non initialisées
├─────────────────────────────────────┤
│ Section .rodata │ ← Données en lecture seule
├─────────────────────────────────────┤
│ Autres sections... │
└─────────────────────────────────────┘
Les bibliothèques Linux suivent une convention stricte :
lib<nom>.so.<version majeure>.<version mineure>.<version patch>
Exemples :
libmath.so.1.2.3libcurl.so.4.6.0libpthread.so.0
libmylib.so.2.5.1
│ │ │ │ │ │
│ │ │ │ │ └─ Patch (corrections de bugs)
│ │ │ │ └─── Mineure (nouvelles fonctionnalités)
│ │ │ └───── Majeure (incompatibilités)
│ │ └──────── Extension Shared Object
│ └───────────── Nom de la bibliothèque
└───────────────── Préfixe standard
Linux utilise des liens symboliques pour la gestion des versions :
libmylib.so.2.5.1 ← Fichier réel
libmylib.so.2 → libmylib.so.2.5.1 (lien symbolique)
libmylib.so → libmylib.so.2 (lien symbolique)
Utilité :
libmylib.so: Utilisé lors de la compilation (développement)libmylib.so.2: Utilisé lors de l'exécution (runtime)libmylib.so.2.5.1: Fichier réel avec version complète
Exemple :
ls -la /usr/lib/x86_64-linux-gnu/libz.so*
lrwxrwxrwx 1 root root 13 libz.so -> libz.so.1.2.11
lrwxrwxrwx 1 root root 13 libz.so.1 -> libz.so.1.2.11
-rw-r--r-- 1 root root 116960 libz.so.1.2.11Contrairement à Windows, pas besoin de directive spéciale. Toutes les fonctions publiques sont exportées par défaut.
library libmylib;
{$mode objfpc}{$H+}
uses
SysUtils;
// Fonction à exporter
function Addition(a, b: Integer): Integer; cdecl; public;
begin
Result := a + b;
end;
// Procédure à exporter
procedure AfficherMessage(msg: PChar); cdecl; public;
begin
WriteLn('Message reçu : ', msg);
end;
// Fonction de version
function GetVersion: PChar; cdecl; public;
begin
Result := '1.0.0';
end;
// Section exports - OBLIGATOIRE
exports
Addition,
AfficherMessage,
GetVersion;
begin
// Initialisation (optionnel)
end.Points clés :
- Convention
cdecl(standard C sur Linux) - Mot-clé
publicpour rendre visible - Directive
exportsliste les symboles exportés - Pas de décoration de noms comme sur Windows
Pour compiler une bibliothèque partagée :
fpc -olibmylib.so libmylib.pasLe compilateur génère automatiquement :
- Le fichier
.soprincipal - Position Independent Code (PIC) nécessaire
fpc -olibmylib.so -CX -XX -Xs libmylib.pasOptions :
-CX: Smart linking-XX: Smart linking plus agressif-Xs: Strip des symboles de débogage
Pour la version de développement :
fpc -g -olibmylib.so libmylib.pasL'option -g inclut les symboles de débogage.
Créez manuellement les liens de version :
# Créer le lien pour le développement
ln -s libmylib.so libmylib.so.1
# Créer le lien pour le runtime
ln -s libmylib.so.1 libmylib.so.1.0.0
# Ou inversement (plus logique)
# Fichier réel
mv libmylib.so libmylib.so.1.0.0
# Liens symboliques
ln -s libmylib.so.1.0.0 libmylib.so.1
ln -s libmylib.so.1 libmylib.soLister les symboles exportés :
nm -D libmylib.so | grep "T "Sortie exemple :
0000000000001150 T Addition
0000000000001170 T AfficherMessage
0000000000001190 T GetVersion
Afficher les informations ELF :
readelf -h libmylib.soVérifier les dépendances :
ldd libmylib.soSortie exemple :
linux-vdso.so.1 (0x00007ffd2e3f1000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8b9c200000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8b9c600000)
program TestLib;
{$mode objfpc}{$H+}
uses
SysUtils;
// Déclaration des fonctions externes
function Addition(a, b: Integer): Integer; cdecl; external 'mylib';
procedure AfficherMessage(msg: PChar); cdecl; external 'mylib';
function GetVersion: PChar; cdecl; external 'mylib';
begin
WriteLn('Version de la bibliothèque : ', GetVersion);
WriteLn('5 + 3 = ', Addition(5, 3));
AfficherMessage('Hello depuis l''application !');
end.Compilation :
fpc -o testlib testlib.pasExécution :
./testlibNote : Le nom 'mylib' sera transformé en libmylib.so automatiquement.
Si la bibliothèque n'est pas dans les chemins standards :
function Addition(a, b: Integer): Integer; cdecl;
external '/home/user/libs/libmylib.so';Ou avec une constante :
const
{$IFDEF CPUX64}
MYLIB_PATH = '/usr/lib/x86_64-linux-gnu/libmylib.so';
{$ELSE}
MYLIB_PATH = '/usr/lib/i386-linux-gnu/libmylib.so';
{$ENDIF}
function Addition(a, b: Integer): Integer; cdecl; external MYLIB_PATH;Pour charger la bibliothèque dynamiquement :
program ChargementDynamique;
{$mode objfpc}{$H+}
uses
SysUtils, dl;
type
TAdditionFunc = function(a, b: Integer): Integer; cdecl;
TGetVersionFunc = function: PChar; cdecl;
var
LibHandle: Pointer;
Addition: TAdditionFunc;
GetVersion: TGetVersionFunc;
Erreur: PChar;
begin
// Charger la bibliothèque
LibHandle := dlopen('libmylib.so', RTLD_LAZY);
if LibHandle = nil then
begin
Erreur := dlerror();
WriteLn('Erreur lors du chargement : ', Erreur);
Halt(1);
end;
try
// Charger les fonctions
Addition := TAdditionFunc(dlsym(LibHandle, 'Addition'));
if Addition = nil then
begin
WriteLn('Fonction Addition non trouvée');
Halt(1);
end;
GetVersion := TGetVersionFunc(dlsym(LibHandle, 'GetVersion'));
if GetVersion = nil then
begin
WriteLn('Fonction GetVersion non trouvée');
Halt(1);
end;
// Utilisation
WriteLn('Version : ', GetVersion);
WriteLn('10 + 15 = ', Addition(10, 15));
finally
// Décharger la bibliothèque
dlclose(LibHandle);
end;
end.Fonctions dlopen :
dlopen(): Charge la bibliothèquedlsym(): Récupère l'adresse d'un symboledlerror(): Récupère le dernier message d'erreurdlclose(): Décharge la bibliothèque
Flags pour dlopen :
RTLD_LAZY: Résolution paresseuse (à la demande)RTLD_NOW: Résolution immédiate (toutes les fonctions)RTLD_GLOBAL: Symboles disponibles pour les bibliothèques chargées aprèsRTLD_LOCAL: Symboles privés (défaut)
Linux cherche les bibliothèques dans cet ordre :
- Répertoires spécifiés dans RPATH (compilé dans l'exécutable)
- Variable d'environnement LD_LIBRARY_PATH
- Répertoires dans /etc/ld.so.conf
- Répertoires système standards :
/lib/usr/lib/lib64/usr/lib64/usr/local/lib
ldconfig -v 2>/dev/null | grep -v ^$'\t'Ajouter temporairement un chemin :
export LD_LIBRARY_PATH=/home/user/mylibs:$LD_LIBRARY_PATH
./mon_programmePour un programme spécifique :
LD_LIBRARY_PATH=/home/user/mylibs ./mon_programmeAttention : Ne modifiez pas LD_LIBRARY_PATH de manière permanente dans .bashrc - c'est une mauvaise pratique qui peut causer des problèmes système.
Intégrer le chemin directement dans l'exécutable :
Avec fpc :
fpc -k'-rpath=/home/user/mylibs' -o testlib testlib.pasVérifier le RPATH :
readelf -d testlib | grep PATHRPATH vs RUNPATH :
- RPATH : Priorité haute, cherché avant LD_LIBRARY_PATH
- RUNPATH : Priorité basse, cherché après LD_LIBRARY_PATH
Pour installer une bibliothèque système :
- Copier la bibliothèque :
sudo cp libmylib.so.1.0.0 /usr/local/lib/- Créer les liens symboliques :
cd /usr/local/lib
sudo ln -s libmylib.so.1.0.0 libmylib.so.1
sudo ln -s libmylib.so.1 libmylib.so- Mettre à jour le cache :
sudo ldconfig- Vérifier :
ldconfig -p | grep mylibCréer /etc/ld.so.conf.d/myapp.conf :
/opt/myapp/lib
Puis :
sudo ldconfigLe SONAME (Shared Object Name) est crucial pour la compatibilité binaire.
Structure :
libmylib.so.MAJOR
Règles de versionnage :
- MAJOR : Incrémenté pour incompatibilité ABI
- MINOR : Incrémenté pour nouvelles fonctionnalités compatibles
- PATCH : Incrémenté pour corrections de bugs
Avec l'éditeur de liens :
fpc -k'-soname,libmylib.so.1' -olibmylib.so.1.0.0 libmylib.pasVérifier le SONAME :
readelf -d libmylib.so.1.0.0 | grep SONAMESortie :
0x000000000000000e (SONAME) Library soname: [libmylib.so.1]
Vous pouvez avoir plusieurs versions majeures simultanément :
/usr/lib/
├── libmylib.so.1.5.2 (ancienne version)
├── libmylib.so.1 → libmylib.so.1.5.2
├── libmylib.so.2.0.0 (nouvelle version)
├── libmylib.so.2 → libmylib.so.2.0.0
└── libmylib.so → libmylib.so.2 (développement)
Les anciennes applications utilisent libmylib.so.1, les nouvelles libmylib.so.2.
#!/bin/bash
# install_lib.sh
LIB_NAME=$1
MAJOR=$2
MINOR=$3
PATCH=$4
FULL_NAME="${LIB_NAME}.so.${MAJOR}.${MINOR}.${PATCH}"
SONAME="${LIB_NAME}.so.${MAJOR}"
LINKNAME="${LIB_NAME}.so"
# Copier le fichier
sudo cp $FULL_NAME /usr/local/lib/
# Créer les liens
cd /usr/local/lib
sudo ln -sf $FULL_NAME $SONAME
sudo ln -sf $SONAME $LINKNAME
# Mettre à jour le cache
sudo ldconfig
echo "Bibliothèque installée :"
ls -l /usr/local/lib/${LIB_NAME}.so*Utilisation :
./install_lib.sh libmylib 1 0 0Par défaut, tous les symboles publics sont exportés. Vous pouvez contrôler cela.
// Visible depuis l'extérieur
function FonctionPublique: Integer; cdecl; public;
// Caché (usage interne uniquement)
function FonctionPrivee: Integer; cdecl;
attribute(visibility('hidden'));Créez un fichier version.map :
VERS_1.0 {
global:
Addition;
AfficherMessage;
GetVersion;
local:
*; # Tout le reste est privé
};
Compilation :
fpc -k'--version-script=version.map' -olibmylib.so libmylib.pasAvantages :
- Réduction de la taille de la table des symboles
- Amélioration des performances de chargement
- Prévention des conflits de symboles
- Meilleure encapsulation
# Tous les symboles
nm -D libmylib.so
# Uniquement les symboles publics
nm -D libmylib.so | grep " T "
# Symboles avec démangling C++
nm -DC libmylib.soTypes de symboles :
T: Code (text section)D: Données initialiséesB: Données non initialisées (BSS)U: Undefined (nécessite une autre bibliothèque)
Le Position Independent Code permet à une bibliothèque d'être chargée à n'importe quelle adresse mémoire.
Pourquoi c'est important :
- Sécurité : ASLR (Address Space Layout Randomization)
- Partage en mémoire entre processus
- Flexibilité du chargeur système
FreePascal génère automatiquement du PIC pour les bibliothèques partagées sur Linux.
Vérifier si une bibliothèque utilise PIC :
readelf -d libmylib.so | grep TEXTRELSi la sortie est vide, c'est bon (PIC activé). Si vous voyez TEXTREL, le PIC n'est pas complet.
fpc -Cg -olibmylib.so libmylib.pasL'option -Cg force la génération PIC.
Le PIC a un léger coût en performance (environ 2-5%) dû à :
- Indirection supplémentaire pour accéder aux données globales
- Utilisation de la GOT (Global Offset Table)
Ce coût est négligeable par rapport aux bénéfices de sécurité et flexibilité.
Créons une bibliothèque complète pour les opérations sur les matrices.
library libmatrix;
{$mode objfpc}{$H+}
uses
SysUtils;
type
PMatrix = ^TMatrix;
TMatrix = record
rows: Integer;
cols: Integer;
data: PDouble;
end;
const
MATRIX_SUCCESS = 0;
MATRIX_ERROR_NULL_POINTER = -1;
MATRIX_ERROR_INVALID_DIMENSIONS = -2;
MATRIX_ERROR_OUT_OF_MEMORY = -3;
var
LastError: Integer = MATRIX_SUCCESS;
LastErrorMsg: string = '';
// Créer une matrice
function matrix_create(rows, cols: Integer): PMatrix; cdecl; public;
var
m: PMatrix;
begin
Result := nil;
LastError := MATRIX_SUCCESS;
if (rows <= 0) or (cols <= 0) then
begin
LastError := MATRIX_ERROR_INVALID_DIMENSIONS;
LastErrorMsg := 'Dimensions invalides';
Exit;
end;
try
New(m);
m^.rows := rows;
m^.cols := cols;
GetMem(m^.data, rows * cols * SizeOf(Double));
FillChar(m^.data^, rows * cols * SizeOf(Double), 0);
Result := m;
except
LastError := MATRIX_ERROR_OUT_OF_MEMORY;
LastErrorMsg := 'Mémoire insuffisante';
end;
end;
// Détruire une matrice
procedure matrix_destroy(m: PMatrix); cdecl; public;
begin
if m = nil then Exit;
if m^.data <> nil then
FreeMem(m^.data);
Dispose(m);
end;
// Définir une valeur
function matrix_set(m: PMatrix; row, col: Integer; value: Double): Integer;
cdecl; public;
begin
LastError := MATRIX_SUCCESS;
Result := MATRIX_SUCCESS;
if m = nil then
begin
LastError := MATRIX_ERROR_NULL_POINTER;
Result := LastError;
Exit;
end;
if (row < 0) or (row >= m^.rows) or (col < 0) or (col >= m^.cols) then
begin
LastError := MATRIX_ERROR_INVALID_DIMENSIONS;
Result := LastError;
Exit;
end;
PDouble(m^.data + (row * m^.cols + col) * SizeOf(Double))^ := value;
end;
// Obtenir une valeur
function matrix_get(m: PMatrix; row, col: Integer; out value: Double): Integer;
cdecl; public;
begin
LastError := MATRIX_SUCCESS;
Result := MATRIX_SUCCESS;
value := 0;
if m = nil then
begin
LastError := MATRIX_ERROR_NULL_POINTER;
Result := LastError;
Exit;
end;
if (row < 0) or (row >= m^.rows) or (col < 0) or (col >= m^.cols) then
begin
LastError := MATRIX_ERROR_INVALID_DIMENSIONS;
Result := LastError;
Exit;
end;
value := PDouble(m^.data + (row * m^.cols + col) * SizeOf(Double))^;
end;
// Addition de matrices
function matrix_add(a, b: PMatrix): PMatrix; cdecl; public;
var
result_matrix: PMatrix;
i, j: Integer;
val_a, val_b: Double;
begin
Result := nil;
LastError := MATRIX_SUCCESS;
if (a = nil) or (b = nil) then
begin
LastError := MATRIX_ERROR_NULL_POINTER;
Exit;
end;
if (a^.rows <> b^.rows) or (a^.cols <> b^.cols) then
begin
LastError := MATRIX_ERROR_INVALID_DIMENSIONS;
LastErrorMsg := 'Dimensions incompatibles';
Exit;
end;
result_matrix := matrix_create(a^.rows, a^.cols);
if result_matrix = nil then Exit;
for i := 0 to a^.rows - 1 do
for j := 0 to a^.cols - 1 do
begin
matrix_get(a, i, j, val_a);
matrix_get(b, i, j, val_b);
matrix_set(result_matrix, i, j, val_a + val_b);
end;
Result := result_matrix;
end;
// Afficher une matrice (pour débogage)
procedure matrix_print(m: PMatrix); cdecl; public;
var
i, j: Integer;
value: Double;
begin
if m = nil then
begin
WriteLn('Matrix is NULL');
Exit;
end;
WriteLn(Format('Matrix %dx%d:', [m^.rows, m^.cols]));
for i := 0 to m^.rows - 1 do
begin
Write('[');
for j := 0 to m^.cols - 1 do
begin
matrix_get(m, i, j, value);
Write(Format('%8.2f', [value]));
if j < m^.cols - 1 then Write(' ');
end;
WriteLn(']');
end;
end;
// Obtenir le dernier code d'erreur
function matrix_get_last_error: Integer; cdecl; public;
begin
Result := LastError;
end;
// Obtenir le dernier message d'erreur
procedure matrix_get_last_error_message(buffer: PChar; size: Integer);
cdecl; public;
begin
if buffer <> nil then
StrLCopy(buffer, PChar(LastErrorMsg), size - 1);
end;
// Version de la bibliothèque
function matrix_version: PChar; cdecl; public;
begin
Result := '1.0.0';
end;
exports
matrix_create,
matrix_destroy,
matrix_set,
matrix_get,
matrix_add,
matrix_print,
matrix_get_last_error,
matrix_get_last_error_message,
matrix_version;
begin
end.fpc -olibmatrix.so.1.0.0 -CX -XX libmatrix.pas
# Créer les liens
ln -s libmatrix.so.1.0.0 libmatrix.so.1
ln -s libmatrix.so.1 libmatrix.soprogram test_matrix;
{$mode objfpc}{$H+}
uses
SysUtils;
type
PMatrix = ^TMatrix;
TMatrix = record
rows: Integer;
cols: Integer;
data: PDouble;
end;
// Déclarations externes
function matrix_create(rows, cols: Integer): PMatrix; cdecl; external 'matrix';
procedure matrix_destroy(m: PMatrix); cdecl; external 'matrix';
function matrix_set(m: PMatrix; row, col: Integer; value: Double): Integer;
cdecl; external 'matrix';
function matrix_get(m: PMatrix; row, col: Integer; out value: Double): Integer;
cdecl; external 'matrix';
function matrix_add(a, b: PMatrix): PMatrix; cdecl; external 'matrix';
procedure matrix_print(m: PMatrix); cdecl; external 'matrix';
function matrix_version: PChar; cdecl; external 'matrix';
var
m1, m2, m3: PMatrix;
begin
WriteLn('libmatrix version : ', matrix_version);
WriteLn;
// Créer deux matrices 2x2
m1 := matrix_create(2, 2);
m2 := matrix_create(2, 2);
// Remplir la première matrice
matrix_set(m1, 0, 0, 1.0);
matrix_set(m1, 0, 1, 2.0);
matrix_set(m1, 1, 0, 3.0);
matrix_set(m1, 1, 1, 4.0);
// Remplir la deuxième matrice
matrix_set(m2, 0, 0, 5.0);
matrix_set(m2, 0, 1, 6.0);
matrix_set(m2, 1, 0, 7.0);
matrix_set(m2, 1, 1, 8.0);
// Afficher
WriteLn('Matrice 1 :');
matrix_print(m1);
WriteLn;
WriteLn('Matrice 2 :');
matrix_print(m2);
WriteLn;
// Addition
m3 := matrix_add(m1, m2);
WriteLn('Matrice 1 + Matrice 2 :');
matrix_print(m3);
// Libération
matrix_destroy(m1);
matrix_destroy(m2);
matrix_destroy(m3);
WriteLn;
WriteLn('Test terminé !');
end.# Compiler le test
fpc -o test_matrix test_matrix.pas
# Exécuter (si la bibliothèque est dans le répertoire courant)
LD_LIBRARY_PATH=. ./test_matrixSortie attendue :
libmatrix version : 1.0.0
Matrice 1 :
[ 1.00 2.00]
[ 3.00 4.00]
Matrice 2 :
[ 5.00 6.00]
[ 7.00 8.00]
Matrice 1 + Matrice 2 :
[ 6.00 8.00]
[ 10.00 12.00]
Test terminé !
Une des forces des bibliothèques Linux est l'interopérabilité parfaite avec C.
Fichier matrix.h :
#ifndef MATRIX_H
#define MATRIX_H
#ifdef __cplusplus
extern "C" {
#endif
// Structure opaque
typedef struct Matrix Matrix;
// Codes d'erreur
#define MATRIX_SUCCESS 0
#define MATRIX_ERROR_NULL_POINTER -1
#define MATRIX_ERROR_INVALID_DIMENSIONS -2
#define MATRIX_ERROR_OUT_OF_MEMORY -3
// Fonctions de la bibliothèque
Matrix* matrix_create(int rows, int cols);
void matrix_destroy(Matrix* m);
int matrix_set(Matrix* m, int row, int col, double value);
int matrix_get(Matrix* m, int row, int col, double* value);
Matrix* matrix_add(Matrix* a, Matrix* b);
void matrix_print(Matrix* m);
int matrix_get_last_error(void);
void matrix_get_last_error_message(char* buffer, int size);
const char* matrix_version(void);
#ifdef __cplusplus
}
#endif
#endif // MATRIX_HFichier test_matrix.c :
#include <stdio.h>
#include <stdlib.h>
#include "matrix.h"
int main() {
Matrix *m1, *m2, *m3;
printf("libmatrix version : %s\n\n", matrix_version());
// Créer les matrices
m1 = matrix_create(2, 2);
m2 = matrix_create(2, 2);
// Remplir m1
matrix_set(m1, 0, 0, 1.0);
matrix_set(m1, 0, 1, 2.0);
matrix_set(m1, 1, 0, 3.0);
matrix_set(m1, 1, 1, 4.0);
// Remplir m2
matrix_set(m2, 0, 0, 5.0);
matrix_set(m2, 0, 1, 6.0);
matrix_set(m2, 1, 0, 7.0);
matrix_set(m2, 1, 1, 8.0);
// Afficher
printf("Matrice 1 :\n");
matrix_print(m1);
printf("\nMatrice 2 :\n");
matrix_print(m2);
// Addition
m3 = matrix_add(m1, m2);
printf("\nMatrice 1 + Matrice 2 :\n");
matrix_print(m3);
// Libération
matrix_destroy(m1);
matrix_destroy(m2);
matrix_destroy(m3);
printf("\nTest terminé !\n");
return 0;
}# Compiler avec liaison dynamique
gcc -o test_matrix_c test_matrix.c -L. -lmatrix -Wl,-rpath,.
# Ou spécifier le chemin de recherche au runtime
gcc -o test_matrix_c test_matrix.c -L. -lmatrix
LD_LIBRARY_PATH=. ./test_matrix_cOptions :
-L.: Chercher les bibliothèques dans le répertoire courant-lmatrix: Lier avec libmatrix.so-Wl,-rpath,.: Ajouter le répertoire courant au RPATH
Fichier test_matrix.py :
#!/usr/bin/env python3
import ctypes
from ctypes import c_int, c_double, c_char_p, POINTER
# Charger la bibliothèque
lib = ctypes.CDLL('./libmatrix.so')
# Définir les types
class Matrix(ctypes.Structure):
pass
MatrixPtr = POINTER(Matrix)
# Déclarer les fonctions
lib.matrix_create.argtypes = [c_int, c_int]
lib.matrix_create.restype = MatrixPtr
lib.matrix_destroy.argtypes = [MatrixPtr]
lib.matrix_destroy.restype = None
lib.matrix_set.argtypes = [MatrixPtr, c_int, c_int, c_double]
lib.matrix_set.restype = c_int
lib.matrix_get.argtypes = [MatrixPtr, c_int, c_int, POINTER(c_double)]
lib.matrix_get.restype = c_int
lib.matrix_add.argtypes = [MatrixPtr, MatrixPtr]
lib.matrix_add.restype = MatrixPtr
lib.matrix_print.argtypes = [MatrixPtr]
lib.matrix_print.restype = None
lib.matrix_version.argtypes = []
lib.matrix_version.restype = c_char_p
# Utilisation
def main():
print(f"libmatrix version : {lib.matrix_version().decode()}\n")
# Créer les matrices
m1 = lib.matrix_create(2, 2)
m2 = lib.matrix_create(2, 2)
# Remplir m1
lib.matrix_set(m1, 0, 0, 1.0)
lib.matrix_set(m1, 0, 1, 2.0)
lib.matrix_set(m1, 1, 0, 3.0)
lib.matrix_set(m1, 1, 1, 4.0)
# Remplir m2
lib.matrix_set(m2, 0, 0, 5.0)
lib.matrix_set(m2, 0, 1, 6.0)
lib.matrix_set(m2, 1, 0, 7.0)
lib.matrix_set(m2, 1, 1, 8.0)
# Afficher
print("Matrice 1 :")
lib.matrix_print(m1)
print("\nMatrice 2 :")
lib.matrix_print(m2)
# Addition
m3 = lib.matrix_add(m1, m2)
print("\nMatrice 1 + Matrice 2 :")
lib.matrix_print(m3)
# Libération
lib.matrix_destroy(m1)
lib.matrix_destroy(m2)
lib.matrix_destroy(m3)
print("\nTest terminé !")
if __name__ == "__main__":
main()Exécution :
chmod +x test_matrix.py
./test_matrix.pyAfficher les dépendances d'une bibliothèque :
ldd libmatrix.soSortie exemple :
linux-vdso.so.1 (0x00007ffc8b3e2000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9a2c400000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9a2c800000)
Signification :
linux-vdso.so.1: Bibliothèque virtuelle du kernellibc.so.6: Bibliothèque C standard/lib64/ld-linux-x86-64.so.2: Chargeur dynamique
Détecter les dépendances manquantes :
ldd libmatrix.so | grep "not found"Lister tous les symboles :
nm libmatrix.soFiltrer les symboles exportés :
nm -D libmatrix.so | grep " T "Avec démangling (pour C++) :
nm -DC libmatrix.soTypes de symboles :
T: Symbole dans la section text (code)D: Symbole dans la section data (données initialisées)B: Symbole dans la section BSS (données non initialisées)U: Symbole non défini (externe)W: Symbole faiblet,d,b: Versions locales (minuscules)
Afficher les informations de l'en-tête :
objdump -f libmatrix.soDésassembler une fonction :
objdump -d libmatrix.so | grep -A 20 "matrix_create"Afficher toutes les sections :
objdump -h libmatrix.soInformations générales :
readelf -h libmatrix.soSymboles dynamiques :
readelf -s libmatrix.soSections :
readelf -S libmatrix.soSegments :
readelf -l libmatrix.soInformations dynamiques :
readelf -d libmatrix.soVoir quelles bibliothèques sont chargées :
strace -e openat ./test_matrix 2>&1 | grep ".so"Tracer tous les appels liés aux fichiers :
strace -e trace=file ./test_matrixltrace ./test_matrixFiltrer pour une bibliothèque spécifique :
ltrace -l libmatrix.so ./test_matrixCompiler avec symboles de débogage :
fpc -g -olibmatrix.so libmatrix.pas
fpc -g -o test_matrix test_matrix.pasDéboguer :
gdb ./test_matrixCommandes GDB utiles :
# Lister les bibliothèques chargées
info sharedlibrary
# Mettre un point d'arrêt dans la bibliothèque
break matrix_create
# Exécuter
run
# Afficher la pile d'appels
backtrace
# Examiner les symboles
info functions matrix_
# Charger les symboles d'une bibliothèque
sharedlibrary libmatrix.soVérifier les fuites :
valgrind --leak-check=full ./test_matrixSortie exemple :
==12345== HEAP SUMMARY:
==12345== in use at exit: 0 bytes in 0 blocks
==12345== total heap usage: 10 allocs, 10 frees, 1,234 bytes allocated
==12345==
==12345== All heap blocks were freed -- no leaks are possible
Options utiles :
--leak-check=full: Détails complets sur les fuites--show-leak-kinds=all: Tous les types de fuites--track-origins=yes: Origine des valeurs non initialisées--verbose: Mode verbeux
Erreur :
error while loading shared libraries: libmatrix.so: cannot open shared object file
Solutions :
# Solution 1 : LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/chemin/vers/lib:$LD_LIBRARY_PATH
# Solution 2 : RPATH
fpc -k'-rpath=/chemin/vers/lib' -o test_matrix test_matrix.pas
# Solution 3 : Installation système
sudo cp libmatrix.so /usr/local/lib/
sudo ldconfig
# Solution 4 : Chemin absolu
# Dans le code
external '/chemin/complet/libmatrix.so';Erreur :
undefined symbol: matrix_create
Diagnostic :
# Vérifier que le symbole existe
nm -D libmatrix.so | grep matrix_createSolutions :
- Vérifier que la fonction est dans la directive
exports - Vérifier l'orthographe exacte
- S'assurer que la convention d'appel est correcte
- Recompiler la bibliothèque
Erreur :
version `GLIBC_2.34' not found
Diagnostic :
ldd --version
ldd libmatrix.soSolutions :
- Compiler sur la version cible du système
- Utiliser des bibliothèques statiques pour certaines dépendances
- Lier statiquement avec libc si nécessaire
Diagnostic avec ldd :
ldd -r libmatrix.soL'option -r affiche les symboles non résolus.
Diagnostic avec GDB :
gdb ./test_matrix
(gdb) run
# Note le backtrace
(gdb) btStructure du répertoire :
libmatrix-1.0.0/
├── DEBIAN/
│ └── control
├── usr/
│ ├── lib/
│ │ ├── libmatrix.so.1.0.0
│ │ ├── libmatrix.so.1 -> libmatrix.so.1.0.0
│ │ └── libmatrix.so -> libmatrix.so.1
│ ├── include/
│ │ └── matrix.h
│ └── share/
│ └── doc/
│ └── libmatrix/
│ ├── README
│ └── copyright
Fichier DEBIAN/control :
Package: libmatrix
Version: 1.0.0
Section: libs
Priority: optional
Architecture: amd64
Depends: libc6 (>= 2.31)
Maintainer: Votre Nom <email@example.com>
Description: Bibliothèque de calcul matriciel
Une bibliothèque pour effectuer des opérations
sur des matrices en FreePascal.
Créer le paquet :
dpkg-deb --build libmatrix-1.0.0Installer :
sudo dpkg -i libmatrix-1.0.0.debDésinstaller :
sudo dpkg -r libmatrixFichier libmatrix.spec :
Name: libmatrix
Version: 1.0.0
Release: 1%{?dist}
Summary: Bibliothèque de calcul matriciel
License: MIT
URL: https://example.com/libmatrix
Source0: libmatrix-%{version}.tar.gz
%description
Une bibliothèque pour effectuer des opérations
sur des matrices en FreePascal.
%prep
%setup -q
%build
fpc -olibmatrix.so.1.0.0 libmatrix.pas
%install
mkdir -p %{buildroot}/usr/lib64
cp libmatrix.so.1.0.0 %{buildroot}/usr/lib64/
ln -s libmatrix.so.1.0.0 %{buildroot}/usr/lib64/libmatrix.so.1
ln -s libmatrix.so.1 %{buildroot}/usr/lib64/libmatrix.so
%files
/usr/lib64/libmatrix.so.1.0.0
/usr/lib64/libmatrix.so.1
/usr/lib64/libmatrix.so
%post
/sbin/ldconfig
%postun
/sbin/ldconfig
%changelog
* Mon Jan 01 2025 Votre Nom <email@example.com> - 1.0.0-1
- Version initialeConstruire :
rpmbuild -ba libmatrix.specCréer un AppImage autonome :
#!/bin/bash
# build_appimage.sh
APP_NAME="MyMatrixApp"
APP_DIR="${APP_NAME}.AppDir"
# Créer la structure
mkdir -p $APP_DIR/usr/{bin,lib}
# Copier l'exécutable
cp test_matrix $APP_DIR/usr/bin/
# Copier les bibliothèques
cp libmatrix.so.1.0.0 $APP_DIR/usr/lib/
ln -s libmatrix.so.1.0.0 $APP_DIR/usr/lib/libmatrix.so.1
# Créer le fichier .desktop
cat > $APP_DIR/${APP_NAME}.desktop << EOF
[Desktop Entry]
Name=MyMatrixApp
Exec=test_matrix
Icon=matrix
Type=Application
Categories=Utility;
EOF
# Créer le fichier AppRun
cat > $APP_DIR/AppRun << 'EOF'
#!/bin/bash
SELF=$(readlink -f "$0")
HERE=${SELF%/*}
export LD_LIBRARY_PATH="${HERE}/usr/lib:${LD_LIBRARY_PATH}"
exec "${HERE}/usr/bin/test_matrix" "$@"
EOF
chmod +x $APP_DIR/AppRun
# Générer l'AppImage
appimagetool $APP_DIRPour distribuer via un PPA Ubuntu :
- Créer un compte Launchpad
- Préparer les fichiers source
- Créer le paquet source :
debuild -S -sa- Uploader vers Launchpad :
dput ppa:votre-compte/ppa libmatrix_1.0.0_source.changesActiver LTO pour de meilleures optimisations :
fpc -olibmatrix.so -CX -XX -O3 -Cg -k'-flto' libmatrix.pasAvantages :
- Inlining entre unités de compilation
- Élimination de code mort
- Optimisations globales
Inconvénients :
- Temps de compilation plus long
- Taille de fichiers intermédiaires plus importante
Réduire la taille au minimum :
# Strip standard
strip --strip-unneeded libmatrix.so
# Strip agressif
strip --strip-all libmatrix.so
# Conserver uniquement les symboles dynamiques
strip --strip-debug libmatrix.soComparaison :
Original : 200 KB
--strip-unneeded : 150 KB (-25%)
--strip-all : 120 KB (-40%)
--strip-debug : 180 KB (-10%)
Compresser avec UPX (Universal Packer for eXecutables) :
upx --best libmatrix.soAttention : Peut poser problème avec certains systèmes de sécurité.
Options recommandées pour production :
fpc -olibmatrix.so \
-O3 \ # Optimisation maximale
-CX \ # Smart linking
-XX \ # Smart linking étendu
-Xs \ # Strip symboles
-Si \ # Inline automatique
-Sc \ # Assertions désactivées
-Sg \ # Goto supporté
-vew \ # Warnings complets
libmatrix.pasPour la taille minimale :
fpc -olibmatrix.so -Os -CX -XX -Xs libmatrix.pasPour la vitesse maximale :
fpc -olibmatrix.so -O3 -Ooregvar -Si libmatrix.pasActiver les protections de sécurité :
fpc -olibmatrix.so \
-k'-Wl,-z,relro' \ # RELRO (Relocation Read-Only)
-k'-Wl,-z,now' \ # Bind now (pas de lazy binding)
-k'-fstack-protector-strong' \ # Protection de pile
libmatrix.pasUtiliser checksec :
checksec --file=libmatrix.soSortie :
RELRO STACK CANARY NX PIE RPATH RUNPATH
Partial RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH
Protections importantes :
- RELRO : Protection contre l'écrasure de la GOT
- Stack Canary : Détection de débordement de pile
- NX : Non-Executable stack
- PIE : Position Independent Executable
- No RPATH : Pas de chemins codés en dur (sécurité)
scanelf -e libmatrix.soScanner les vulnérabilités connues :
# Analyser les dépendances
ldd libmatrix.so | while read lib; do
dpkg -S $(echo $lib | awk '{print $3}') 2>/dev/null
done
# Vérifier les CVE connus
sudo apt install apt-listbugs
apt-listbugs list libmatrixCompiler pour différentes architectures :
64 bits (x86_64) :
fpc -Px86_64 -olibmatrix.so libmatrix.pas32 bits (i386) :
fpc -Pi386 -olibmatrix.so.32 libmatrix.pasARM 64 bits (aarch64) :
fpc -Paarch64 -olibmatrix.so.arm64 libmatrix.pasStructure :
/usr/lib/
├── x86_64-linux-gnu/
│ └── libmatrix.so.1.0.0
├── i386-linux-gnu/
│ └── libmatrix.so.1.0.0
└── aarch64-linux-gnu/
└── libmatrix.so.1.0.0
Depuis Ubuntu x86_64 vers ARM :
# Installer le cross-compilateur
sudo apt install fpc-3.2.2-arm-linux
# Compiler
fpc -Parm -olibmatrix.so.arm libmatrix.pas- Utiliser
cdeclpour la compatibilité C - Suivre les conventions de nommage (lib*.so.*)
- Définir un SONAME approprié
- Créer les liens symboliques correctement
- Documenter l'API (fichiers .h)
- Gérer les erreurs sans exceptions
- Tester sur plusieurs distributions
- Utiliser ldconfig après installation
- Fournir des fichiers pkg-config
- Versionner sémantiquement
- Exporter des types Pascal complexes
- Oublier d'exécuter ldconfig
- Modifier l'ABI sans changer SONAME
- Utiliser RPATH avec chemins absolus
- Négliger la thread-safety
- Ignorer les warnings du compilateur
- Oublier les symboles de débogage en dev
- Ne pas tester la liaison dynamique
- Distribuer sans documentation
- Coder en dur des chemins système
Avant de distribuer votre bibliothèque partagée :
- Testée sur Ubuntu, Debian, Fedora, Arch
- Versions 32 et 64 bits si nécessaire
- SONAME correctement défini
- Liens symboliques créés
- Fichiers .h fournis pour C/C++
- Documentation API complète
- Exemples de code pour plusieurs langages
- Fichier pkg-config (.pc)
- Tests automatisés
- Pas de fuites mémoire (Valgrind)
- Protections de sécurité activées
- Paquet .deb ou .rpm disponible
- Licence clairement définie
- Changelog maintenu
Créer des Shared Objects sur Linux avec FreePascal permet :
- Intégration parfaite dans l'écosystème Linux
- Compatibilité avec tous les langages via l'ABI C
- Distribution facile via les gestionnaires de paquets
- Performance native optimale
Points essentiels à retenir :
- ✅ Convention
cdeclobligatoire - ✅ Nommage
lib*.so.*standard - ✅ SONAME pour la gestion des versions
- ✅ Liens symboliques appropriés
- ✅ Installation via ldconfig
- ✅ Documentation multilingue
Avec ces connaissances, vous pouvez créer des bibliothèques partagées Linux professionnelles, robustes et largement utilisables dans tout l'écosystème open source.