Comment puis-je imprimer la version de construction du programme (située dans la section .note.gnu.build-id elf) à partir du programme lui-même ?
-
Vous devez lire le
ElfW(Ehdr)
(au début du fichier) pour trouver les en-têtes de programme dans votre binaire (.e_phoff
et.e_phnum
vous indiquera où se trouvent les en-têtes de programme et combien d'entre eux doivent être lus). -
Vous lisez ensuite les en-têtes de programme, jusqu'à ce que vous trouviez
PT_NOTE
partie de votre programme. Ce segment vous indiquera le décalage par rapport au début de toutes les notes de votre binaire. -
Vous devez ensuite lire le
ElfW(Nhdr)
et ignorez le reste de la note (la taille totale de la note est desizeof(Nhdr) + .n_namesz + .n_descsz
, correctement aligné), jusqu'à ce que vous trouviez une note avec.n_type == NT_GNU_BUILD_ID
. -
Une fois que vous avez trouvé
NT_GNU_BUILD_ID
note, sautez son.n_namesz
, et lisez le.n_descsz
octets pour lire le build-id réel.
Vous pouvez vérifier que vous lisez les bonnes données en comparant ce que vous lisez avec la sortie de readelf -n a.out
.
PS
Si vous allez avoir du mal à décoder build-id comme ci-dessus, et if votre exécutable n'est pas dépouillé, il peut être préférable pour vous de simplement décoder et imprimer le symbole noms à la place (c'est-à-dire pour répliquer ce que backtrace_symbols
fait) - c'est en fait plus facile à faire que de décoder les notes ELF, car la table des symboles contient des entrées de taille fixe.
Fondamentalement, c'est le code que j'ai écrit en fonction de la réponse donnée à ma question. Afin de compiler le code, j'ai dû apporter quelques modifications et j'espère que cela fonctionnera pour autant de types de plates-formes que possible. Cependant, il n'a été testé que sur une seule machine de construction. L'une des hypothèses que j'ai utilisées était que le programme était construit sur la machine qui l'exécute, donc inutile de vérifier la compatibilité de l'endianness entre le programme et la machine.
[email protected]:~/$ uname -s -r -m -o
Linux 3.2.0-45-generic x86_64 GNU/Linux
[email protected]:~/$ g++ test.cpp -o test
[email protected]:~/$ readelf -n test | grep Build
Build ID: dc5c4682e0282e2bd8bc2d3b61cfe35826aa34fc
[email protected]:~/$ ./test
Build ID: dc5c4682e0282e2bd8bc2d3b61cfe35826aa34fc
#include <elf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#if __x86_64__
# define ElfW(type) Elf64_##type
#else
# define ElfW(type) Elf32_##type
#endif
/*
detecting build id of a program from its note section
http://stackoverflow.com/questions/17637745/can-a-program-read-its-own-elf-section
http://www.scs.stanford.edu/histar/src/pkg/uclibc/utils/readelf.c
http://www.sco.com/developers/gabi/2000-07-17/ch5.pheader.html#note_section
*/
int main (int argc, char* argv[])
{
char *thefilename = argv[0];
FILE *thefile;
struct stat statbuf;
ElfW(Ehdr) *ehdr = 0;
ElfW(Phdr) *phdr = 0;
ElfW(Nhdr) *nhdr = 0;
if (!(thefile = fopen(thefilename, "r"))) {
perror(thefilename);
exit(EXIT_FAILURE);
}
if (fstat(fileno(thefile), &statbuf) < 0) {
perror(thefilename);
exit(EXIT_FAILURE);
}
ehdr = (ElfW(Ehdr) *)mmap(0, statbuf.st_size,
PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(thefile), 0);
phdr = (ElfW(Phdr) *)(ehdr->e_phoff + (size_t)ehdr);
while (phdr->p_type != PT_NOTE)
{
++phdr;
}
nhdr = (ElfW(Nhdr) *)(phdr->p_offset + (size_t)ehdr);
while (nhdr->n_type != NT_GNU_BUILD_ID)
{
nhdr = (ElfW(Nhdr) *)((size_t)nhdr + sizeof(ElfW(Nhdr)) + nhdr->n_namesz + nhdr->n_descsz);
}
unsigned char * build_id = (unsigned char *)malloc(nhdr->n_descsz);
memcpy(build_id, (void *)((size_t)nhdr + sizeof(ElfW(Nhdr)) + nhdr->n_namesz), nhdr->n_descsz);
printf(" Build ID: ");
for (int i = 0 ; i < nhdr->n_descsz ; ++i)
{
printf("%02x",build_id[i]);
}
free(build_id);
printf("\n");
return 0;
}