7

ビルド情報をバイナリに追加するために、ld の --build-id オプションを使用したいと考えています。ただし、この情報をプログラム内で利用できるようにする方法がわかりません。例外が発生するたびにバックトレースを書き込むプログラムと、この情報を解析するスクリプトを書きたいとします。スクリプトはプログラムのシンボル テーブルを読み取り、バックトレースに出力されたアドレスを検索します (プログラムが静的にリンクされ、backtrace_symbols が機能しないため、このようなスクリプトを使用せざるを得ません)。スクリプトが正しく機能するためには、プログラムのビルド バージョンと、バックトレースを作成したプログラムのビルド バージョンを一致させる必要があります。プログラム自体から (.note.gnu.build-id elf セクションにある) プログラムのビルド バージョンを印刷するにはどうすればよいですか?

4

3 に答える 3

5

プログラム自体から (.note.gnu.build-id elf セクションにある) プログラムのビルド バージョンを印刷するにはどうすればよいですか?

  1. ElfW(Ehdr)バイナリ内のプログラム ヘッダーを見つけるには、(ファイルの先頭にある)を読み取る必要があり.e_phoffます(プログラム.e_phnumヘッダーの場所と、読み取る必要があるヘッダーの数がわかります)。

  2. PT_NOTE次に、プログラムのセグメントが見つかるまで、プログラム ヘッダーを読み取り ます。そのセグメントは、バイナリ内のすべてのノートの先頭へのオフセットを示します。

  3. 次に を読み、メモの残りの部分をスキップする必要がありますElfW(Nhdr)(メモの合計サイズは でsizeof(Nhdr) + .n_namesz + .n_descsz、適切に配置されています) .n_type == NT_GNU_BUILD_ID

  4. メモを見つけたらNT_GNU_BUILD_ID、その をスキップして.n_namesz.n_descszバイトを読み取って実際のビルド ID を読み取ります。

読み取ったデータを の出力と比較することで、正しいデータを読み取っていることを確認できますreadelf -n a.out

PS

上記のように build-id をデコードするのに苦労し、実行可能ファイルが削除されていない場合は、代わりにシンボル名をデコードして出力する (つまり、何を行うかを複製するbacktrace_symbols)方が良いかもしれません。シンボルテーブルには固定サイズのエントリが含まれているため、ELF ノートをデコードするよりも簡単です。

于 2013-07-15T06:27:56.963 に答える
3

基本的に、これは私の質問に対する回答に基づいて作成したコードです。コードをコンパイルするためにいくつかの変更を加える必要がありましたが、できるだけ多くの種類のプラットフォームで機能することを願っています。ただし、1 つのビルド マシンでのみテストされました。私が使用した仮定の 1 つは、プログラムはそれを実行するマシン上でビルドされているため、プログラムとマシン間のエンディアンの互換性をチェックしても意味がないというものでした。

user@:~/$ uname -s -r -m -o
Linux 3.2.0-45-generic x86_64 GNU/Linux
user@:~/$ g++ test.cpp -o test
user@:~/$ readelf -n test | grep Build
    Build ID: dc5c4682e0282e2bd8bc2d3b61cfe35826aa34fc
user@:~/$ ./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;
}
于 2013-07-19T07:06:53.370 に答える