2013-07-14 9 views
6

Chciałbym użyć opcji ld's --build-id w celu dodania informacji o kompilacji do mojego pliku binarnego. Jednak nie jestem pewien, jak udostępnić te informacje w programie. Załóżmy, że chcę napisać program, który zapisuje ślad za każdym razem, gdy wystąpi wyjątek, i skrypt, który analizuje te informacje. Skrypt odczytuje tabelę symboli programu i wyszukuje adresy wydrukowane w backtrace (jestem zmuszony użyć takiego skryptu, ponieważ program jest statycznie połączony i backtrace_symbols nie działa). Aby skrypt działał poprawnie, muszę dopasować wersję kompilacji programu do wersji build programu, który utworzył ślad. Jak mogę wydrukować wersję kompilacji programu (znajdującą się w sekcji .note.gnu.build-id elf) z samego programu?Czy program może czytać własną sekcję elfa?

Odpowiedz

5

Jak mogę wydrukować wersję kompilacji programu (znajdującą się w sekcji .note.gnu.build-id elf) z samego programu?

  1. Trzeba czytać ElfW(Ehdr) (na początku pliku), aby znaleźć nagłówki programu w binarnym (.e_phoff i .e_phnum powie Ci, gdzie nagłówki programowe są i ilu z nich, aby przeczytać) .

  2. Następnie czytasz nagłówki programów, dopóki nie znajdziesz odcinka PT_NOTE twojego programu. Ten segment powie Ci przesunięcie do początku wszystkich notatek w twoim pliku binarnym.

  3. Następnie należy odczytać ElfW(Nhdr) i pominąć resztę Note (całkowity rozmiar banknotu jest sizeof(Nhdr) + .n_namesz + .n_descsz, prawidłowo ustawione), aż znajdziesz notatkę z .n_type == NT_GNU_BUILD_ID.

  4. Po znalezieniu notatki NT_GNU_BUILD_ID pomiń jej numer .n_namesz i przeczytaj bajty .n_descsz, aby odczytać rzeczywisty identyfikator kompilacji.

Można sprawdzić, czy czytasz odpowiednie dane, porównując to, co czytasz z wyjściem readelf -n a.out.

P.S.

Jeśli zamierzasz przejść przez kłopotów zdekodować gromadzeniu id jak wyżej, a jeśli plik wykonywalny nie jest usuwany, może być lepiej, aby po prostu zdekodować i druk symbol nazwy zamiast (tj replikuj co robi backtrace_symbols) - jest to łatwiejsze niż dekodowanie notatek ELF, ponieważ tablica symboli zawiera wpisy o ustalonej wielkości.

+0

dzięki za odpowiedzi. – e271p314

2

Zasadniczo jest to kod, który napisałem na podstawie odpowiedzi udzielonej na moje pytanie. Aby skompilować kod, musiałem wprowadzić pewne zmiany i mam nadzieję, że będzie działać na tylu typach platform, jak to tylko możliwe. Został jednak przetestowany tylko na jednej maszynie. Jednym z założeń, którego używałem, było to, że program został zbudowany na komputerze, który go uruchamia, więc nie ma sensu sprawdzać zgodności endianów między programem a maszyną.

[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; 
} 
Powiązane problemy