562 lines
18 KiB
C
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);
|
|
}
|
|
|