Suivez-nous sur X
|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : Les objets partagés sur AmigaOS 4 - présentation et utilisation
(Article écrit par Mathias Parnaudeau et extrait de www.gurumed.net - juillet 2009)
|
|
Jusqu'à présent, les dépendances d'un programme étaient connues en tant que bibliothèques statiques (.a) ou partagées
(.library). Après les systèmes Unix, AmigaOS 4 introduit un nouveau type nommé "objets partagées" (ou "shared objects",
de suffixe .so) dont des détails techniques se trouvent sur la page
des Frieden. Cet article se propose de présenter les objets partagés et de montrer comment les utiliser et les générer, en
se servant d'un cas concret, SDL_ttf.
Nature d'un objet partagé
Un programme ou une bibliothèque contient du code et des données qui occupent des zones distinctes en mémoire. Le code est
en lecture seule et donc plusieurs processus pourraient utiliser le code présent en un seul endroit. C'est le cas des
bibliothèques partagées sauf que leur création implique de faire très attention au niveau des données, elles n'acceptent
pas de variables globales qui pourraient être utilisées par plusieurs programmes sans dommages. Les objets partagés proposent
un format capable de partager le code et d'allouer une zone de données pour chacun des programmes utilisant un objet.
A l'heure actuelle, le code n'est pas encore partagé.
Mise en place
Mon but était d'utiliser les objets partagés pour compiler PointRider (lecteur de fichiers PowerPoint) dont les dépendances
sont : SDL, SDL_image et SDL_ttf. La dernière n'étant disponible qu'en bibliothèque statique, je me suis dit qu'elle serait
parfaite pour s'exercer. Elle est de plus très simple et après avoir téléchargé l'archive sur la page du projet SDL_ttf,
il n'y a qu'un seul fichier à compiler. La complexité repose dans la bibliothèque freetype dont elle dépend.
Création d'un objet partagé
En théorie, un objet partagé possède les avantages d'une ".library" à l'exécution et ceux d'une bibliothèque statique à
la création, qui est la partie la plus simple. Pour créer une bibliothèque statique, on faisait :
gcc -O2 -I/SDK/Local/newlib/include/SDL -c SDL_ttf.c
ar ru libSDL_ttf.a SDL_ttf.o
|
Pour un objet partagé, la procédure est très proche, la compilation tout d'abord :
gcc -O2 -I/SDK/Local/newlib/include/SDL -fPIC -c SDL_ttf.c
|
Attention, les majuscules dans l'argument "-fPIC" ont leur importance, ne saisissez pas "-fpic".
Pour la création de l'objet final, l'option "-shared" est obligatoire. Il faut également indiquer tous les objets à "linker"
donc les fichiers ".o" mais dans notre cas, il faut également mentionner "-lfreetype". Je ne l'avais pas fait dans un premier
temps et c'est la catastrophe assurée car sinon toute référence à l'objet freetype est perdue. Comme on traite d'objets partagés,
gcc va chercher libfreetype.so et comme elle se trouve par défaut dans SOBJS: dans AmigaOS 4, nous ajoutons ce chemin de
recherche à ligne de commande (au format Unix) :
gcc -L/SOBJS/ -shared -o libSDL_ttf.so SDL_ttf.o -lfreetype
|
Que faire quand on a obtenu ce fichier ? Un utilisateur qui voudra lancer un programme qui le requiert aura dû le placer au
préalable dans le répertoire système SOBJS:.
Utilisation pratique
Le problème est que lorsqu'on veut compiler un programme qui utilise des objets partagés, il n'a que faire de SOBJS:,
le compilateur considère ses propres chemins dans lesquels il s'attend à trouver les bibliothèques, comme "SDK:newlib/lib"
ou "SDK:Local/newlib/lib" puisque newlib est désormais la libc choisie par défaut dans AmigaOS 4.1 aux dépens de clib2 précédemment.
Pour utiliser un objet partagé, il faut que ses dépendances en soient aussi. Par exemple, si un programme requiert libSDL_ttf
qui repose sur libfreetype, alors si libSDL_ttf est un objet partagé, libfreetype doit l'être aussi.
Basiquement, pour dire au compilateur d'utiliser des objets partagés au lieu de bibliothèques statiques, il faut indiquer
l'argument "-use-dynld" au niveau de l'édition de liens. Cette option signifie "dynamic load" ou "chargement dynamique"
car c'est à l'exécution du programme que l'objet est chargé et que les liens sont établis.
Voici un exemple de code utilisant SDL_ttf :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "SDL.h"
#include "SDL_ttf.h"
#include "SDL_syswm.h"
int main( int argc, char* argv[] )
{
SDL_Surface* screen;
SDL_Event event;
int ttf_init;
int ask_exit = 0;
TTF_Font *default_font = NULL;
int font_size;
char font_path[256];
SDL_Color color = {255,0,0};
SDL_Surface *text_surface;
SDL_Rect rect;
/* Initialize SDL and its TTF module */
SDL_Init(SDL_INIT_VIDEO);
ttf_init = TTF_Init();
if (ttf_init == -1){
printf("TTF_Init: %s\n", TTF_GetError());
}
/* Get a font that we know it is available in the system */
strcpy(font_path, "SYS:TTFonts/Vera.ttf");
strcpy(font_path, "SYS:Fonts/_TrueType/Vera.ttf");
font_size = 48;
printf("TTF_WasInit = %d\n", TTF_WasInit());
default_font = TTF_OpenFont(font_path, font_size);
if (!default_font){
printf("Error : can't find font [%s], error = %s\n", font_path, TTF_GetError());
return 1;
}
/* Open a surface and render the text */
screen = SDL_SetVideoMode(640, 480, 0, 0);
SDL_WM_SetCaption("This example uses libSDL_ttf.so", "example"); // 2nd arg is for icon name
text_surface = TTF_RenderText_Solid(default_font, "Amiga rulez !", color);
if (text_surface){
rect.x = 20;
rect.y = 20;
rect.w = screen->w - 20;
rect.h = text_surface->h;
SDL_BlitSurface(text_surface, NULL, screen, &rect);
SDL_FreeSurface(text_surface);
}else{
// Red rectangle to show that an error happened
rect.x = 20;
rect.y = 20;
rect.w = screen->w - 40;
rect.h = 80;
SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, 255, 0, 0));
printf("Can't render the solid text, error = %s\n", TTF_GetError());
}
SDL_UpdateRect(screen, 0, 0, 0, 0);
/* Event loop */
while (!ask_exit)
{
/* Look for an event */
if (SDL_WaitEvent(&event)) {
/* An event was found */
switch (event.type) {
case SDL_QUIT:
/* Close button clicked */
ask_exit = 1;
break;
case SDL_KEYDOWN:
switch ((int)event.key.keysym.sym) {
case SDLK_ESCAPE:
ask_exit = 1;
break;
}
break;
}
}
}
/* Close the font and SDL */
if (default_font){
TTF_CloseFont(default_font);
}
TTF_Quit();
SDL_Quit();
return 0;
}
|
Pour le compiler :
gcc -Wall -L/SOBJS -ISDK:Local/newlib/include/SDL -ISDK:Local/common/include/SDL
-Wl,--verbose -use-dynld -o example example.c -lSDL-1.2 -lSDL_ttf -lfreetype -lz
|
Attention, si dans ses chemins de recherche, le compilateur trouve une version statique de la bibliothèque à lier, il s'en
contentera et la choisira avant de tomber sur l'objet partagé qui serait dans un chemin additionnel, comme SOBJS:.
C'est pourquoi il nécessaire dans ce cas de créer un lien, depuis SDK:Local/newlib/lib :
makelink FROM libSDL_ttf.so TO SOBJS:libSDL_ttf.so SOFT
|
Si l'option commençant par "-Wl" ne vous est pas familière, sachez que c'est une méthode pour passer des paramètres à l'éditeur de
lien. "--verbose" est ici pour l'exemple, il est probable que vous ne gardiez pas cette option, gêné par le flot d'informations.
Dans d'autres exemples ici ou là, vous trouverez certainement "-Wl,-rpath,/SDK/Local/newlib/lib". Cela ajoute un chemin pour
chercher les objets partagés dans un répertoire additionnel à l'exécution du programme, le "r" dans "rpath" signifiant "runtime".
Personnellement, je déconseille cet usage, les utilisateurs n'ont pas forcément le SDK installé.
La réduction de la taille de l'exécutable est visible mais ce n'est pas une méthode sûre pour vérifier l'utilisation prévue des
objets partagés. La commande suivante permet d'avoir les idées plus claires :
Ce qui donne :
Dynamic section at offset 0xc78 contains 20 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libSDL-1.2.so]
0x00000001 (NEEDED) Shared library: [libSDL_ttf.so]
0x00000001 (NEEDED) Shared library: [libfreetype.so]
0x00000001 (NEEDED) Shared library: [libz.so]
0x00000001 (NEEDED) Shared library: [libgcc.so]
0x00000001 (NEEDED) Shared library: [libc.so]
0x00000004 (HASH) 0x10000e8
0x00000005 (STRTAB) 0x10003a0
0x00000006 (SYMTAB) 0x10001b0
0x0000000a (STRSZ) 423 (bytes)
0x0000000b (SYMENT) 16 (bytes)
0x00000015 (DEBUG) 0x0
0x00000003 (PLTGOT) 0x1010d70
0x00000002 (PLTRELSZ) 228 (bytes)
0x00000014 (PLTREL) RELA
0x00000017 (JMPREL) 0x1000560
0x00000007 (RELA) 0x1000548
0x00000008 (RELASZ) 252 (bytes)
0x00000009 (RELAENT) 12 (bytes)
0x00000000 (NULL) 0x0
|
On constate avec les lignes de type "NEEDED" que toutes les dépendances sont des objets partagés, comme prévu.
Les contreparties
Les objets partagés présents dans le système ne contiennent pas leur numéro de version, c'est ennuyeux pour le suivi. Il y a
ensuite des fichiers qui n'ont pas le même nom et pour lesquels il faut créer des liens symboliques. Par exemple, l'objet SDL
se nomme "libSDL-1.2.so" mais un lien "libSDL.so" existe pour les programmes qui chercheraient l'objet sous ce nom. Et
quand une nouvelle version sortira, le lien pourra par exemple pointer sur "libSDL-1.3.so".
Comme nous le disions au début, pour l'instant chaque programme charge le code et les données de ses objets partagés : le partage
n'est pour l'instant pas effectif.
L'avantage tient dans le fait que le portage de bibliothèques, venant de Linux par exemple, est facilité. Et comme les
bibliothèques statiques, il n'y a pas besoin de les ouvrir et de les fermer.
A noter que si un programme ne trouve pas un objet dont il a besoin, le système ouvre une fenêtre en indiquant quel objet manque
puis un message "file is not executable" sera affiché dans une console.
|