2013-07-14 10 views
6

Vorrei utilizzare l'opzione --dot -build di ld per aggiungere informazioni di build al mio binario. Tuttavia, non sono sicuro di come rendere queste informazioni disponibili all'interno del programma. Supponiamo di voler scrivere un programma che scriva un backtrace ogni volta che si verifica un'eccezione e uno script che analizzi queste informazioni. Lo script legge la tabella dei simboli del programma e cerca gli indirizzi stampati nel backtrace (sono obbligato a usare tale script perché il programma è collegato staticamente e backtrace_symbols non funziona). Affinché lo script funzioni correttamente, devo abbinare la versione build del programma con la versione build del programma che ha creato il backtrace. Come posso stampare la versione build del programma (che si trova nella sezione elfo .note.gnu.build-id) dal programma stesso?un programma può leggere la propria sezione elfo?

risposta

5

Come posso stampare la versione di build del programma (che si trova nella sezione .note.gnu.build-id elf) dal programma stesso?

  1. è necessario leggere il ElfW(Ehdr) (all'inizio del file) per trovare le intestazioni di programma nel vostro binario (.e_phoff e .e_phnum vi dirà dove le intestazioni del programma sono, e quanti di loro a leggere) .

  2. Quindi leggi le intestazioni del programma, finché non trovi il segmento PT_NOTE del tuo programma. Quel segmento ti dirà offset rispetto all'inizio di tutte le note nel tuo binario.

  3. È quindi necessario leggere il ElfW(Nhdr) e saltare il resto della nota (dimensione totale della nota è sizeof(Nhdr) + .n_namesz + .n_descsz, allineato correttamente), fino a trovare una nota con .n_type == NT_GNU_BUILD_ID.

  4. Una volta trovato NT_GNU_BUILD_ID nota, saltare oltre la sua .n_namesz, e leggere i .n_descsz byte da leggere l'attuale accumulo di id.

È possibile verificare che si sta leggendo i dati giusti confrontando ciò che si legge con l'uscita di readelf -n a.out.

P.S.

Se avete intenzione di passare attraverso la briga di decodificare l'accumulo di id come sopra, e se l'eseguibile non è spogliato, può essere meglio per voi di decodificare solo e stampare simbolo nomi invece (vale a dire replicare ciò che fa backtrace_symbols) - in realtà è più facile da fare rispetto alla decodifica delle note ELF, perché la tabella dei simboli contiene voci di dimensioni fisse.

+0

grazie per la risposta. – e271p314

2

Fondamentalmente, questo è il codice che ho scritto in base alla risposta data alla mia domanda. Per compilare il codice ho dovuto apportare alcune modifiche e spero che funzionerà per il maggior numero possibile di piattaforme. Tuttavia, è stato testato solo su una macchina di compilazione. Una delle ipotesi che ho usato è che il programma è stato costruito sulla macchina che lo esegue, quindi non ha alcun senso nel controllare la compatibilità endianness tra il programma e la macchina.

[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; 
} 
Problemi correlati