2013-07-14 21 views
6

Binamıma yapı bilgisi eklemek için ld's --build-id seçeneğini kullanmak istiyorum. Ancak, bu bilgiyi programın içinde nasıl hazırlayacağımı bilmiyorum. Bir istisna her gerçekleştiğinde bir backtrace yazan bir program yazmak istiyorum ve bu bilgiyi ayrıştıran bir betik yazalım. Komut, programın sembol tablosunu okur ve backtrace'de yazdırılan adresleri arar (program statik olarak bağlandığından ve backtrace_symbols çalışmadığı için böyle bir betik kullanmaya zorlanıyorum). Komut dosyasının düzgün çalışması için, programın sürümünün, backtrace'i oluşturan programın derleme sürümüyle eşleşmesi gerekir. Programın oluşturulmuş sürümünü (.note.gnu.build-id elf bölümünde bulunan) programın kendisinden nasıl yazdırabilirim?bir program kendi elf bölümünü okuyabilir mi?

cevap

5
Programdan kendisinden (.note.gnu.build kimliği elf bölümünde bulunan) Programın inşa sürümünü yazdırmak nasıl

?

  1. Okumak gerek ElfW(Ehdr) program başlıkları bulmak için (dosyasının başında), kayıt ikili (.e_phoff ve .e_phnum programı başlıkları nerede söyleyecektir ve bunların nasıl okunacağını çok) .

  2. Programınızın PT_NOTE bölümünü bulana kadar program başlıklarını okuyun. Bu bölüm size ikili dosyanızdaki tüm notaların başlangıcına denk geleceğini söyleyecektir.

  3. Daha sonra .n_type == NT_GNU_BUILD_ID içeren bir not buluncaya kadar ElfW(Nhdr) okuyup notu geri kalanını (not toplam boyutu, düzgün hizalanmış sizeof(Nhdr) + .n_namesz + .n_descsz olan) atlamak gerekir.

  4. Not NT_GNU_BUILD_ID notunu bulduktan sonra, .n_namesz numarasını atlayın ve gerçek yapı kimliğini okumak için .n_descsz bayt değerini okuyun.

Sen sen readelf -n a.out çıkışı ile okuduklarım karşılaştırarak doğru verileri okuduğunuz olduğunu doğrulayabilir.

P.S. çalıştırılabilir elimden değilse sadece deşifre ve baskı sembolü isimleri yerine etmek için

Eğer yukarıdaki gibi birikmesi id çözmek için zahmete girsin yapacaksanız

ve , bu (yani daha iyi olabilir backtrace_symbols'un ne yaptığını yineleyin) - sembol tablosunun sabit boyutlu girişler içerdiğinden, ELF notlarının kodunu çözmekten daha kolaydır.

+0

teşekkürler. – e271p314

2

Temel olarak, bu, soruma verilen cevaba dayanarak yazdığım kod. Kodu derlemek için bazı değişiklikler yapmak zorunda kaldım ve umarım mümkün olduğu kadar çok platform türü için çalışır. Ancak, sadece bir yapı makinesinde test edildi. Kullandığım varsayımlardan biri, programın makine üzerinde çalıştığı ve program ile makine arasındaki endianite uyumluluğunu kontrol etmenin bir anlamı olmadığıydı. cevaplanması için

[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; 
} 
İlgili konular