Comme j'avais le même besoin de récupérer tous les noms de symboles chargés lors de l'exécution, j'ai fait des recherches basées sur la réponse de R.. Voici donc une solution détaillée pour les bibliothèques partagées Linux au format ELF qui fonctionne avec mon gcc 4.3.4, mais j'espère aussi avec les versions plus récentes.
J'ai principalement utilisé les sources suivantes pour développer cette solution :
- Page de manuel ELF
- Un exemple de code (trouvé lors de la recherche de "dl_iterate_phdr")
Et voici mon code. J'ai utilisé des noms de variables explicites et ajouté des commentaires détaillés pour le rendre compréhensible. Si quelque chose ne va pas ou manque, s'il vous plaît faites le moi savoir ... (Edit:je viens de réaliser que la question était pour C et que mon code est pour C++. Mais si vous omettez le vecteur et la chaîne, cela devrait également fonctionner pour C )
#include <link.h>
#include <string>
#include <vector>
using namespace std;
/* Callback for dl_iterate_phdr.
* Is called by dl_iterate_phdr for every loaded shared lib until something
* else than 0 is returned by one call of this function.
*/
int retrieve_symbolnames(struct dl_phdr_info* info, size_t info_size, void* symbol_names_vector)
{
/* ElfW is a macro that creates proper typenames for the used system architecture
* (e.g. on a 32 bit system, ElfW(Dyn*) becomes "Elf32_Dyn*") */
ElfW(Dyn*) dyn;
ElfW(Sym*) sym;
ElfW(Word*) hash;
char* strtab = 0;
char* sym_name = 0;
ElfW(Word) sym_cnt = 0;
/* the void pointer (3rd argument) should be a pointer to a vector<string>
* in this example -> cast it to make it usable */
vector<string>* symbol_names = reinterpret_cast<vector<string>*>(symbol_names_vector);
/* Iterate over all headers of the current shared lib
* (first call is for the executable itself) */
for (size_t header_index = 0; header_index < info->dlpi_phnum; header_index++)
{
/* Further processing is only needed if the dynamic section is reached */
if (info->dlpi_phdr[header_index].p_type == PT_DYNAMIC)
{
/* Get a pointer to the first entry of the dynamic section.
* It's address is the shared lib's address + the virtual address */
dyn = (ElfW(Dyn)*)(info->dlpi_addr + info->dlpi_phdr[header_index].p_vaddr);
/* Iterate over all entries of the dynamic section until the
* end of the symbol table is reached. This is indicated by
* an entry with d_tag == DT_NULL.
*
* Only the following entries need to be processed to find the
* symbol names:
* - DT_HASH -> second word of the hash is the number of symbols
* - DT_STRTAB -> pointer to the beginning of a string table that
* contains the symbol names
* - DT_SYMTAB -> pointer to the beginning of the symbols table
*/
while(dyn->d_tag != DT_NULL)
{
if (dyn->d_tag == DT_HASH)
{
/* Get a pointer to the hash */
hash = (ElfW(Word*))dyn->d_un.d_ptr;
/* The 2nd word is the number of symbols */
sym_cnt = hash[1];
}
else if (dyn->d_tag == DT_STRTAB)
{
/* Get the pointer to the string table */
strtab = (char*)dyn->d_un.d_ptr;
}
else if (dyn->d_tag == DT_SYMTAB)
{
/* Get the pointer to the first entry of the symbol table */
sym = (ElfW(Sym*))dyn->d_un.d_ptr;
/* Iterate over the symbol table */
for (ElfW(Word) sym_index = 0; sym_index < sym_cnt; sym_index++)
{
/* get the name of the i-th symbol.
* This is located at the address of st_name
* relative to the beginning of the string table. */
sym_name = &strtab[sym[sym_index].st_name];
symbol_names->push_back(string(sym_name));
}
}
/* move pointer to the next entry */
dyn++;
}
}
}
/* Returning something != 0 stops further iterations,
* since only the first entry, which is the executable itself, is needed
* 1 is returned after processing the first entry.
*
* If the symbols of all loaded dynamic libs shall be found,
* the return value has to be changed to 0.
*/
return 1;
}
int main()
{
vector<string> symbolNames;
dl_iterate_phdr(retrieve_symbolnames, &symbolNames);
return 0;
}
Sur les systèmes basés sur ELF à liaison dynamique, vous pouvez avoir une fonction dl_iterate_phdr
disponible. Si tel est le cas, il peut être utilisé pour collecter des informations sur chaque fichier de bibliothèque partagée chargé, et les informations que vous obtenez sont suffisantes pour examiner les tables de symboles. Le processus est essentiellement :
- Obtenir l'adresse des en-têtes de programme à partir du
dl_phdr_info
la structure vous est renvoyée. - Utilisez le
PT_DYNAMIC
en-tête de programme pour trouver le_DYNAMIC
tableau pour le module. - Utilisez le
DT_SYMTAB
,DT_STRTAB
, etDT_HASH
entrées de_DYNAMIC
pour trouver la liste des symboles.DT_HASH
n'est nécessaire que pour obtenir la longueur de la table des symboles, car elle ne semble pas être stockée ailleurs.
Les types dont vous avez besoin doivent tous être en <elf.h>
et <link.h>
.
Ce n'est pas vraiment spécifique au C, mais au système d'exploitation et au format binaire et (pour le débogage des symboles et des noms de symboles C++ non mutilés) même à une question spécifique au compilateur. Il n'y a pas de méthode générique, ni de méthode véritablement élégante.
Le moyen le plus portable et le plus évolutif consiste probablement à exécuter un programme externe tel que nm
, qui est en POSIX. La version GNU trouvée dans Linux a probablement un tas d'extensions, que vous devriez éviter si vous visez la portabilité et la pérennité.
Sa sortie doit rester stable, et même si les formats binaires changent, il sera également mis à jour et continuera à fonctionner. Exécutez-le simplement avec les bons commutateurs, capturez sa sortie (probablement en l'exécutant via popen
pour éviter un fichier temporaire) et analysez-le.