libver/lib/libver.c

562 lines
18 KiB
C
Raw Permalink Normal View History

2000-07-28 17:39:02 +02:00
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
2003-01-17 08:52:42 +01:00
#ifdef __linux
2000-07-28 17:39:02 +02:00
#else
#include <libelf.h>
#endif
#include <dlfcn.h>
#include <libver.h>
2000-07-28 17:39:02 +02:00
VER_INFO_EXPORT (libver, "$Revision: 1.3 $", "$Name: $", __FILE__, "$Author: agibert $")
2000-07-28 17:39:02 +02:00
void VER_Error_Print (void);
void * VER_Symbol_Next_Find (const char *, VERT_Object *, VERT_Index *);
extern char * strdup (const char *);
2003-01-17 08:52:42 +01:00
#ifdef __linux
2000-07-28 17:39:02 +02:00
extern struct r_scope_elem *_dl_global_scope [2];
#endif
/*------------------------------------------------------------------------------*/
/* Cr<43>ation d'un conteneur d'informations */
/*------------------------------------------------------------------------------*/
/* (O) Contanier : adresse d'un pointeur sur le container <20> cr<63>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 <20> jour d'une information */
/*------------------------------------------------------------------------------*/
/* (I) Container : pointeur sur le conteneur d'informations */
/* (I) Info_Type : type d'information <20> mettre <20> 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 <20> 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<75>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 <20> 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<72>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 <EFBFBD> ce qui est charg<EFBFBD> par le loader
au lancement du programme : programme principal + objets statiques (.o)
- autant d'objets qui ont charg<EFBFBD>s dynamiquement par le linker (.so)
*/
2003-01-17 08:52:42 +01:00
#ifdef __linux
2000-07-28 17:39:02 +02:00
/*
Sous LINUX, le linker d<EFBFBD>finit une variable globale bien pratique qui r<EFBFBD>f<EFBFBD>rence
la liste des objets link<EFBFBD>s dynamiquement.
*/
2003-01-17 08:52:42 +01:00
Object = (VERT_Object *)((struct r_scope_elem *)(_dl_global_scope [0]))->r_list [0];
2000-07-28 17:39:02 +02:00
#else
/*
Sous SOLARIS, on doit faire appel <EFBFBD> la fonction dlinfo() pour obtenir
les informations de link pour l'objet principal.
NB : cette fonction n'existe pas sous LINUX.
*/
/* On commence par r<>cup<75>rer l'objet principal */
Handle = dlopen (NULL, RTLD_LAZY);
2000-07-28 17:39:02 +02:00
Object = (VERT_Object *)malloc (sizeof (VERT_Object));
dlinfo (Handle, RTLD_DI_LINKMAP, &Object);
#endif
/* Recherche d'informations dans tous les objets link<6E>s */
Index = 0;
while (Object != NULL)
{
if (VER_Info_Next_Get (&Container, Object, &Index) == VERS_OK)
{
Nb_Object++;
/*
D<EFBFBD>finition du nom de l'objet : on ajoute '.o' ou '.so'
selon qu'il s'agit du premier objet charg<EFBFBD> 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<75>r<EFBFBD>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<72>te apr<70>s l'objet principal */
if (Object->l_prev == NULL && Mode == VERD_MINIMAL)
Object = NULL;
else
{
/* Recherche dans l'objet suivant */
2003-01-17 08:52:42 +01:00
Object = (VERT_Object *)(Object->l_next);
2000-07-28 17:39:02 +02:00
Index = 0;
}
}
}
return Nb_Object;
}
/*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------*/
/* Fonctions priv<69>es */
/*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------*/
/* Recherche d'un symbole dans les tables de symboles des objets link<6E>s : */
/* */
/* (I) Symbol_Name : nom de symbole <20> rechercher */
/* (O) Object : objet dans lequel on recherche le symbole */
/* (I) Index : pointeur sur l'index <20> partir duquel on recherche */
/* */
/* Retourne la r<>f<EFBFBD>rence du symbole trouv<75>, NULL si non trouv<75>. */
/*------------------------------------------------------------------------------*/
void * VER_Symbol_Next_Find (const char * Symbol_Name, VERT_Object * Object, VERT_Index * Index)
{
unsigned long Offset;
/* Recherche dans l'objet pass<73> en param<61>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<70>ter les adresses virtuelles */
/*
Les adresses donn<EFBFBD>es dans les sections sont virtuelles : on doit leur ajouter
un offset d<EFBFBD>fini dans l'ent<EFBFBD>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<61>s */
/* Recherche des sections relatives aux symboles */
2003-01-17 08:52:42 +01:00
#ifdef __linux
2000-07-28 17:39:02 +02:00
/* Sous LINUX, les sections sont directement accessibles par le champs <l_info> de l'ent<6E>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 <EFBFBD>tre retrouv<EFBFBD> <EFBFBD> partir de l'ent<EFBFBD>te de l'objet.
En effet, l'ent<EFBFBD>te contient un champ <l_buckets> qui pointe sur la donn<EFBFBD>e <bucket> de la section HASH.
Connaissant la structure de l'ent<EFBFBD>te de la section HASH, on retrouve facilement la donn<EFBFBD>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<EFBFBD>ressent.
*/
i = 0;
Found = 0;
while (Found < 3)
{
switch ((int)Object->l_ld [i].d_tag)
{
case DT_HASH:
/* La section HASH nous int<6E>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<6E>e <nchain> */
Found++;
break;
case DT_SYMTAB:
/* La section SYMTAB r<>f<EFBFBD>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<EFBFBD>nes de caract<EFBFBD>res qui sont r<EFBFBD>f<EFBFBD>renc<EFBFBD>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<6E>ressent pas */
}
i++;
}
#endif
/* Recherche dans la table des symboles <20> partir de l'index pass<73> en param<61>tre */
while ((*Index) < Nb_Symbol)
{
Symbol = &Symbol_Table [*Index];
/* On recherche une fonction qui correspond au nom pass<73> en param<61>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 <20>t<EFBFBD> trouv<75> */
return NULL;
}
return NULL;
}
/*------------------------------------------------------------------------------*/
/* Routine d'affichage d'un message d'erreur */
/*------------------------------------------------------------------------------*/
void VER_Error_Print (void)
{
fprintf (stderr, VER_Error_Msg);
}