libver/lib/libver.c
2000-07-28 15:39:02 +00:00

562 lines
18 KiB
C

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifdef LINUX
#else
#include <libelf.h>
#endif
#include <dlfcn.h>
#include <ver.h>
VER_INFO_EXPORT (libver, "$Revision: 1.1 $", "$Name: $", __FILE__, "$Author: smas $")
void VER_Error_Print (void);
void * VER_Symbol_Next_Find (const char *, VERT_Object *, VERT_Index *);
extern char * strdup (const char *);
#ifdef LINUX
extern struct r_scope_elem *_dl_global_scope [2];
#endif
/*------------------------------------------------------------------------------*/
/* Création d'un conteneur d'informations */
/*------------------------------------------------------------------------------*/
/* (O) Contanier : adresse d'un pointeur sur le container à créer */
/* (I) Object_Name : nom de l'objet */
/*------------------------------------------------------------------------------*/
VERT_Status VER_Info_Init (VERT_Info_Container ** Container, const char * Object_Name)
{
*Container = (VERT_Info_Container *) malloc (sizeof (VERT_Info_Container));
if (*Container == NULL)
{
sprintf (VER_Error_Msg, "Error VER_Info_Init : unable to allocate memory for a container");
VER_Error_Print ();
return VERS_ERRMEM;
}
if (Object_Name != NULL)
(*Container)->name = strdup (Object_Name);
else
(*Container)->name = NULL;
(*Container)->version = NULL;
(*Container)->tag = NULL;
(*Container)->srcfile = NULL;
(*Container)->author = NULL;
return VERS_OK;
}
/*------------------------------------------------------------------------------*/
/* Mise à jour d'une information */
/*------------------------------------------------------------------------------*/
/* (I) Container : pointeur sur le conteneur d'informations */
/* (I) Info_Type : type d'information à mettre à jour */
/* (I) Value : valeur de l'information */
/*------------------------------------------------------------------------------*/
VERT_Status VER_Info_Set (VERT_Info_Container * Container, VERT_Info Info_Type, const char * Value)
{
char * tmp;
if (Container == NULL)
{
sprintf (VER_Error_Msg, "Error VER_Info_Set : the container is null");
VER_Error_Print ();
return VERS_ERRAPI;
}
if (Value == NULL)
{
sprintf (VER_Error_Msg, "Error VER_Info_Set : the value is null");
VER_Error_Print ();
return VERS_ERRAPI;
}
switch ((int)Info_Type)
{
case VERD_VERSION:
if (!strncmp (Value, "$Revision: ", 11))
{
char * ptr;
tmp = strdup (Value + 11);
if ((ptr = strchr (tmp, ' ')) != NULL) *ptr = '\0';
}
else
tmp = strdup (Value);
if (!tmp) return VERS_ERRMEM;
if (Container->version != NULL) free (Container->version);
Container->version = tmp;
break;
case VERD_TAG:
if (!strncmp (Value, "$Name: ", 7))
{
char * ptr;
tmp = strdup (Value + 7);
if ((ptr = strchr (tmp, ' ')) != NULL) *ptr = '\0';
}
else
tmp = strdup (Value);
if (!tmp) return VERS_ERRMEM;
if (Container->tag != NULL) free (Container->tag);
Container->tag = tmp;
break;
case VERD_SRCFILE:
tmp = strdup (Value);
if (!tmp) return VERS_ERRMEM;
if (Container->srcfile != NULL) free (Container->srcfile);
Container->srcfile = tmp;
break;
case VERD_AUTHOR:
if (!strncmp (Value, "$Author: ", 9))
{
char * ptr;
tmp = strdup (Value + 9);
if (!tmp) return VERS_ERRMEM;
if ((ptr = strchr (tmp, ' ')) != NULL) *ptr = '\0';
}
else
tmp = strdup (Value);
if (!tmp) return VERS_ERRMEM;
if (Container->author != NULL) free (Container->author);
Container->author = tmp;
break;
default:
sprintf (VER_Error_Msg, "Error VER_Info_Set : unexpected type %d", (int)Info_Type);
VER_Error_Print ();
return VERS_ERRAPI;
}
return VERS_OK;
}
/*------------------------------------------------------------------------------*/
/* Effacement d'une information */
/*------------------------------------------------------------------------------*/
/* (I) Container : pointeur sur le conteneur d'informations */
/* (I) Info_Type : type d'information à effacer */
/*------------------------------------------------------------------------------*/
VERT_Status VER_Info_Clean (VERT_Info_Container * Container, VERT_Info Info_Type)
{
if (Container == NULL)
{
sprintf (VER_Error_Msg, "Error VER_Info_Clean : the container is null");
VER_Error_Print ();
return VERS_ERRAPI;
}
switch ((int)Info_Type)
{
case VERD_VERSION:
if (Container->version != NULL) free (Container->version);
break;
case VERD_TAG:
if (Container->tag != NULL) free (Container->tag);
break;
case VERD_SRCFILE:
if (Container->srcfile != NULL) free (Container->srcfile);
break;
case VERD_AUTHOR:
if (Container->author != NULL) free (Container->author);
break;
default:
sprintf (VER_Error_Msg, "Error VER_Info_Clean : unexpected type %d", (int)Info_Type);
VER_Error_Print ();
return VERS_ERRAPI;
}
return VERS_ERRAPI;
}
/*------------------------------------------------------------------------------*/
/* Destruction d'un container d'informations */
/*------------------------------------------------------------------------------*/
/* (I) Container : pointeur sur le conteneur d'informations */
/*------------------------------------------------------------------------------*/
VERT_Status VER_Info_End (VERT_Info_Container * Container)
{
if (Container == NULL)
{
sprintf (VER_Error_Msg, "Error VER_Info_End : the container is null");
VER_Error_Print ();
return VERS_ERRAPI;
}
if (Container->name != NULL) free (Container->name);
if (Container->version != NULL) free (Container->version);
if (Container->tag != NULL) free (Container->tag);
if (Container->srcfile != NULL) free (Container->srcfile);
if (Container->author != NULL) free (Container->author);
free (Container);
return VERS_OK;
}
/*------------------------------------------------------------------------------*/
/* Affichage des informations d'un conteneur */
/*------------------------------------------------------------------------------*/
/* (I) Stream : pointeur sur le flux de sortie */
/* (I) Container : pointeur sur le conteneur d'informations */
/* (I) Mode : mode d'affichage */
/*------------------------------------------------------------------------------*/
VERT_Status VER_Info_Print (FILE * Stream, VERT_Info_Container * Container, VERT_Print_Mode Mode)
{
if (Stream == NULL)
{
sprintf (VER_Error_Msg, "Error VER_Info_Print : the file descriptor is null");
VER_Error_Print ();
return VERS_ERRAPI;
}
if (Container == NULL)
{
sprintf (VER_Error_Msg, "Error VER_Info_Print : the container is null");
VER_Error_Print ();
return VERS_ERRAPI;
}
if (Mode == VERD_MINIMAL)
fprintf (Stream, "%-20s%-20s%-30s\n",
Container->name ? Container->name : "???",
Container->version ? Container->version : "",
Container->tag ? Container->tag : "");
else
fprintf (Stream, "%-20s%-20s%-30s%-30s%-20s\n",
Container->name ? Container->name : "???",
Container->version ? Container->version : "",
Container->tag ? Container->tag : "",
Container->srcfile ? Container->srcfile : "",
Container->author ? Container->author : "");
return VERS_OK;
}
/*------------------------------------------------------------------------------*/
/* Récupération d'un conteneur d'informations */
/*------------------------------------------------------------------------------*/
/* (O) Container : adresse d'un pointeur sur le conteneur d'informations */
/* (I) Object : objet dans lequel on effectue la recherche */
/* (I) Index : pointeur d'index à partir duquel on effectue la recherche */
/*------------------------------------------------------------------------------*/
VERT_Status VER_Info_Next_Get (VERT_Info_Container ** Container, VERT_Object * Object, VERT_Index * Index)
{
void * Ptr = VER_Symbol_Next_Find (VER_FCN_NAME, Object, Index);
VERT_Fcn * Ptr_Fcn = (VERT_Fcn *)Ptr;
if (Ptr == NULL) return VERS_KO;
*Container = (VERT_Info_Container *)(*Ptr_Fcn)();
if (*Container == NULL) return VERS_ERRMEM;
return VERS_OK;
}
/*------------------------------------------------------------------------------*/
/* Affichage des informations exportées par l'objet courant */
/*------------------------------------------------------------------------------*/
/* (I) Stream : pointeur sur le flux de sortie */
/* (I) Mode : mode d'affichage */
/*------------------------------------------------------------------------------*/
VERT_Status VER_Object_Print (FILE * Stream, VERT_Print_Mode Mode)
{
VERT_Status Nb_Object = 0;
VERT_Info_Container * Container;
VERT_Object * Object;
char sep [120];
VERT_Index Index;
void * Handle;
if (Stream == NULL)
{
sprintf (VER_Error_Msg, "Error VER_Object_Print : the file descriptor is null");
VER_Error_Print ();
return VERS_ERRAPI;
}
/* Affichage sous forme de tableau en option 'verbose' */
if (Mode == VERD_VERBOSE)
{
memset (sep, '-', sizeof (sep));
sep [sizeof (sep) -1] = '\0';
fprintf (stdout, "%-20s%-20s%-30s%-30s%-20s\n%s\n", "OBJECT NAME", "VERSION", "TAG", "SOURCE FILE", "AUTHOR", sep);
}
/* Info : on distingue deus types d'objets :
- un premier objet (objet principal) qui correspond à ce qui est chargé par le loader
au lancement du programme : programme principal + objets statiques (.o)
- autant d'objets qui ont chargés dynamiquement par le linker (.so)
*/
/* On commence par récupèrer l'objet principal */
Handle = dlopen (NULL, RTLD_LAZY);
#ifdef LINUX
/*
Sous LINUX, le linker définit une variable globale bien pratique qui référence
la liste des objets linkés dynamiquement.
*/
Object = ((struct r_scope_elem *)(_dl_global_scope [0]))->r_list [0];
#else
/*
Sous SOLARIS, on doit faire appel à la fonction dlinfo() pour obtenir
les informations de link pour l'objet principal.
NB : cette fonction n'existe pas sous LINUX.
*/
Object = (VERT_Object *)malloc (sizeof (VERT_Object));
dlinfo (Handle, RTLD_DI_LINKMAP, &Object);
#endif
/* Recherche d'informations dans tous les objets linkés */
Index = 0;
while (Object != NULL)
{
if (VER_Info_Next_Get (&Container, Object, &Index) == VERS_OK)
{
Nb_Object++;
/*
Définition du nom de l'objet : on ajoute '.o' ou '.so'
selon qu'il s'agit du premier objet chargé ou non.
*/
if (Container->name)
{
if (Object->l_prev != NULL)
{
if ((Container->name = (char *)realloc (Container->name, strlen (Container->name) + 4)) != NULL)
sprintf (Container->name, "%s.so", Container->name);
}
else
{
if ((Container->name = (char *)realloc (Container->name, strlen (Container->name) + 3)) != NULL)
sprintf (Container->name, "%s.o", Container->name);
}
}
/* Affichage complet des informations récupérées */
VER_Info_Print (Stream, Container, Mode);
/* Désallocation des informations */
VER_Info_End (Container);
/* Recherche du symbole suivant dans le même objet */
Index++;
}
else
{
/* En mode affichage minimal, on s'arrête après l'objet principal */
if (Object->l_prev == NULL && Mode == VERD_MINIMAL)
Object = NULL;
else
{
/* Recherche dans l'objet suivant */
Object = Object->l_next;
Index = 0;
}
}
}
return Nb_Object;
}
/*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------*/
/* Fonctions privées */
/*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------*/
/* Recherche d'un symbole dans les tables de symboles des objets linkés : */
/* */
/* (I) Symbol_Name : nom de symbole à rechercher */
/* (O) Object : objet dans lequel on recherche le symbole */
/* (I) Index : pointeur sur l'index à partir duquel on recherche */
/* */
/* Retourne la référence du symbole trouvé, NULL si non trouvé. */
/*------------------------------------------------------------------------------*/
void * VER_Symbol_Next_Find (const char * Symbol_Name, VERT_Object * Object, VERT_Index * Index)
{
unsigned long Offset;
/* Recherche dans l'objet passé en paramètre */
if (Object != NULL)
{
VERT_Index * Hash_Table, Nb_Symbol;
Elf32_Sym * Symbol_Table;
char * String_Table;
Elf32_Sym * Symbol;
int i, Found;
/* Définition de l'offset pour interpréter les adresses virtuelles */
/*
Les adresses données dans les sections sont virtuelles : on doit leur ajouter
un offset défini dans l'entête de l'objet. Ceci n'est toutefois pas vrai pour l'objet principal.
*/
if (Object->l_prev == NULL)
Offset = 0; /* Pas d'offset pour l'objet principal */
else
Offset = Object->l_addr; /* Offset non nul pour les objets partagés */
/* Recherche des sections relatives aux symboles */
#ifdef LINUX
/* Sous LINUX, les sections sont directement accessibles par le champs <l_info> de l'entête de l'objet */
Symbol_Table = (Elf32_Sym *)(Object->l_info [DT_SYMTAB]->d_un.d_ptr);
String_Table = (char *)(Object->l_info [DT_STRTAB]->d_un.d_ptr);
/*
Le nombre de symboles peut lui-aussi être retrouvé à partir de l'entête de l'objet.
En effet, l'entête contient un champ <l_buckets> qui pointe sur la donnée <bucket> de la section HASH.
Connaissant la structure de l'entête de la section HASH, on retrouve facilement la donnée <nchain>.
*/
Nb_Symbol = *(VERT_Index *)((size_t)(Object->l_buckets) - sizeof (VERT_Index));
#else
/*
Sous SOLARIS, le champs <l_info> n'existe pas.
Il faut donc parcourir toutes les sections pour trouver celles qui nous intéressent.
*/
i = 0;
Found = 0;
while (Found < 3)
{
switch ((int)Object->l_ld [i].d_tag)
{
case DT_HASH:
/* La section HASH nous intéressent car elle contient le nombre de symboles */
Hash_Table = (VERT_Index *)(Object->l_ld [i].d_un.d_ptr + Offset);
Nb_Symbol = Hash_Table [1]; /* Donnée <nchain> */
Found++;
break;
case DT_SYMTAB:
/* La section SYMTAB référence les symboles de l'objet courant */
Symbol_Table = (Elf32_Sym *)(Object->l_ld [i].d_un.d_ptr + Offset);
Found++;
break;
case DT_STRTAB:
/*
La section STRTAB contient les chaînes de caractères qui sont référencées
par les autres sections (notamment les noms de symboles).
*/
String_Table = (char *)(Object->l_ld [i].d_un.d_ptr + Offset);
Found++;
break;
default:
/* Les autres sections ne nous intéressent pas */
}
i++;
}
#endif
/* Recherche dans la table des symboles à partir de l'index passé en paramètre */
while ((*Index) < Nb_Symbol)
{
Symbol = &Symbol_Table [*Index];
/* On recherche une fonction qui correspond au nom passé en paramètre */
if (Symbol->st_value != 0 && ELF32_ST_TYPE (Symbol->st_info) <= STT_FUNC && !strncmp (String_Table + Symbol->st_name, Symbol_Name, strlen (Symbol_Name)))
return (void *)(Symbol->st_value + Offset);
(*Index)++;
}
/* Aucun symbole n'a été trouvé */
return NULL;
}
return NULL;
}
/*------------------------------------------------------------------------------*/
/* Routine d'affichage d'un message d'erreur */
/*------------------------------------------------------------------------------*/
void VER_Error_Print (void)
{
fprintf (stderr, VER_Error_Msg);
}