¿Cómo puedo imprimir la versión de compilación del programa (que se encuentra en la sección .note.gnu.build-id elf) desde el propio programa?
-
Tienes que leer el
ElfW(Ehdr)
(al principio del archivo) para encontrar encabezados de programa en su binario (.e_phoff
y.e_phnum
le dirá dónde están los encabezados del programa y cuántos de ellos leer). -
Luego lee los encabezados del programa, hasta que encuentre
PT_NOTE
segmento de su programa. Ese segmento le indicará el desplazamiento al comienzo de todas las notas en su binario. -
A continuación, debe leer el
ElfW(Nhdr)
y omita el resto de la nota (el tamaño total de la nota essizeof(Nhdr) + .n_namesz + .n_descsz
, correctamente alineado), hasta que encuentre una nota con.n_type == NT_GNU_BUILD_ID
. -
Una vez que encuentres
NT_GNU_BUILD_ID
nota, pase por alto su.n_namesz
y lea el.n_descsz
bytes para leer el ID de compilación real.
Puede verificar que está leyendo los datos correctos comparando lo que lee con el resultado de readelf -n a.out
.
PD
Si se va a tomar la molestia de decodificar el ID de compilación como se indicó anteriormente, y si su ejecutable no está eliminado, puede ser mejor para usted simplemente decodificar e imprimir símbolo nombres en su lugar (es decir, para replicar lo que backtrace_symbols
hace):en realidad es más fácil de hacer que decodificar notas ELF, porque la tabla de símbolos contiene entradas de tamaño fijo.
Básicamente, este es el código que he escrito basado en la respuesta dada a mi pregunta. Para compilar el código tuve que hacer algunos cambios y espero que funcione para tantos tipos de plataformas como sea posible. Sin embargo, se probó solo en una máquina de construcción. Una de las suposiciones que usé fue que el programa se creó en la máquina que lo ejecuta, por lo que no tiene sentido verificar la compatibilidad de endianness entre el programa y la máquina.
[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;
}