#include #include #include #ifdef __linux #else #include #endif #include #include VER_INFO_EXPORT (libver, "$Revision: 1.3 $", "$Name: $", __FILE__, "$Author: agibert $") 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) */ #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 = (VERT_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. */ /* On commence par récupèrer l'objet principal */ Handle = dlopen (NULL, RTLD_LAZY); 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 = (VERT_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 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 qui pointe sur la donnée de la section HASH. Connaissant la structure de l'entête de la section HASH, on retrouve facilement la donnée . */ Nb_Symbol = *(VERT_Index *)((size_t)(Object->l_buckets) - sizeof (VERT_Index)); #else /* Sous SOLARIS, le champs 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 */ 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); }