FPGA開発日記

FPGAというより、コンピュータアーキテクチャかもね! カテゴリ別記事インデックス https://sites.google.com/site/fpgadevelopindex/

libbfdを使ってバイナリから情報を吸い出す方法の調査

http://www.skyfree.org/linux/references/bfd.pdf

libbfdを使えば、バイナリから情報を引き出し、プログラム中で利用することができる。これを使えば、今開発している命令セットシミュレータに、バイナリの情報を読み込んで、デバッグ情報を出力しながらシミュレーションを実行することができる。 また、現在開発中のluaインタフェースを結合しながら、luaでシミュレータを制御しながら、シミュレーション中にバイナリのデバッグ情報を使いながらシミュレーションができるようになる。

例えば、

  • ある変数にアクセスする(グローバル変数の名前をバイナリから逆サーチしてアドレスを算出する)と、ブレークするようにする
  • ある変数に10回アクセスするまでシミュレーションを繰替えす

ということが可能になる。

libbfdのインストール

まずは、libbfdをインストールする方法から調べよう。libbfdは、Ubuntuの環境ならば以下のようにしてインストールすることができる。

sudo aptitude install binutils-dev

libbfdを組み込んでコンパイルする場合は、-lbfdをgccのオプションとして追加する。

libbfdファーストインプレッション

普通のやつらの下を行け: BFDでデバッグ情報の取得 - bkブログ

ここらへんを参考にしている。

void show_debug_info (void *address)
{
    asection *section = bfd_get_section_by_name(abfd, ".debug_info");
    assert(section != NULL);

    const char *file_name;
    const char *function_name;
    unsigned int lineno;
    int found = bfd_find_nearest_line(abfd, section, symbols,
                                      (long)address,
                                      &file_name,
                                      &function_name,
                                      &lineno);
    if (found && file_name != NULL && function_name != NULL) {
        char tmp[strlen(file_name)];
        strcpy(tmp, file_name);
        printf("%s:%s:%d\n", basename(tmp), function_name, lineno);
    }
}

デバッグ情報をバイナリ(上記のコードではabfdというbfd情報)から抽出して、アドレスaddressに最も近い行数を探索している。

上記リンク先のプログラムをコンパイルするためには、以下のようにコンパイルする。

gcc -g bfd_openr.c -o bfd_openr -lbfd

-gを追加することにより、デバッグ情報が追加され、bfd_get_section_by_name()でデバッグ情報を抽出できるようになる。 -lbfdは、/usr/lib/libbfd.soをリンクしている。

libbfdを使って、バイナリ中のシンボル情報を出力してみる

以下のように作った。

#include <assert.h>
#include <bfd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>

#include <stdint.h>

static bfd *abfd;
static asymbol **symbols;
static int nsymbols;


int main (int argc, char *argv[])
{
    abfd = bfd_openr (argv[1], NULL);

    fprintf (stdout, "Filename = %s\n", abfd->filename);

    bfd_check_format (abfd, bfd_object);

    long storage_needed;
    asymbol **symbol_table;
    long number_of_symbols;
    long i;

    storage_needed = bfd_get_symtab_upper_bound (abfd);
    if (storage_needed < 0) {
        fprintf (stderr, "Error storage_needed < 0\n");
        return 0;
    }
    if (storage_needed == 0) {
        fprintf (stderr, "Error storage_needed == 0\n");
        return 0;
    }

    symbol_table = (asymbol **) malloc (storage_needed);

    number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table);

    fprintf (stdout, "number_of_symbols=%ld\n", number_of_symbols);

    if (number_of_symbols < 0) {
        fprintf (stderr, "Error: number_of_symbols < 0\n");
        return 0;
    }
    for (i = 0; i < number_of_symbols; i++) {
        asection *section = symbol_table[i]->section;

        fprintf (stdout, "SymbolName=%s : ", bfd_asymbol_name (symbol_table[i]));
        if ((symbol_table[i]->flags & BSF_FUNCTION) != 0x00) {
            fprintf (stdout, "BSF_Function ");
        } else if ((symbol_table[i]->flags & BSF_LOCAL) != 0x00) {
            fprintf (stdout, "BSF_Local ");
        } else if ((symbol_table[i]->flags & BSF_GLOBAL) != 0x00) {
            fprintf (stdout, "BSF_global ");
        } else {
            fprintf (stdout, "BSF_others ");
        }

        fprintf (stdout, "%08x %08x\n", bfd_asymbol_base (symbol_table[i]), bfd_asymbol_value (symbol_table[i]));
    }


    return 0;
}
  • bfd_openr () : バイナリを読み込んで、bfd情報を抽出する。
  • bfd_check_format () : bfdのフォーマットをチェックする。
  • bfd_get_symtab_upper_bound () : シンボルテーブルをロードするために、ロードする先のメモリの大きさを計算する。storage_neededの分だけmallocでメモリを確保する。
  • bfd_cannonicalize_symtab () : 確保したメモリ領域に、シンボル情報をロードする。

number_of_symbolsに、シンボルリストの数が格納されるので、その分だけ後続のfor文をロードする。

  • bfd_asymbol_name() : シンボルの名前を読み込む。
  • bfd_asymbol_base() : シンボルの存在しているセクションのベースアドレスを取得する。
  • bfd_asymbol_value() : シンボルの存在するアドレス情報を出力する。

for文中のcaseでは、フラグ(つまり、シンボルの型)に応じて出力する内容を変更している。

gcc bfd_openr_test.c -o bfd_openr_test -lbfd

このbfd情報はバイナリ間で互換性が取れているため、たとえ上記のプログラムをx86Linux上でコンパイルしたとしても、例えばRISC-V用にクロスコンパイルしたバイナリファイルを解析することができる。 例えば、RISC-VバイナリとしてコンパイルしたCoremarkのシンボル情報を出力してみる。

./bfd_openr_test ~/benchmarks/coremark_v1.0/coremark.bin 
Filename = /home/vagrant/benchmarks/coremark_v1.0/coremark.bin
number_of_symbols=90
SymbolName=.text.reset : BSF_Local 00000100 00000100
SymbolName=.data : BSF_Local 7f000000 7f000000
SymbolName=.sdata : BSF_Local 7f00000c 7f00000c
SymbolName=.bss : BSF_Local 7f000018 7f000018
SymbolName=.sbss : BSF_Local 7f0007e8 7f0007e8
SymbolName=.text : BSF_Local 80000000 80000000
SymbolName=.text.startup : BSF_Local 80002034 80002034
SymbolName=.eh_frame : BSF_Local 8000276c 8000276c
SymbolName=.rodata : BSF_Local 80002c08 80002c08
SymbolName=.rodata.str1.4 : BSF_Local 80002ebc 80002ebc
SymbolName=.debug_info : BSF_Local 00000000 00000000
SymbolName=.debug_abbrev : BSF_Local 00000000 00000000
SymbolName=.debug_loc : BSF_Local 00000000 00000000
SymbolName=.debug_aranges : BSF_Local 00000000 00000000
SymbolName=.debug_ranges : BSF_Local 00000000 00000000
SymbolName=.debug_line : BSF_Local 00000000 00000000
SymbolName=.debug_str : BSF_Local 00000000 00000000
SymbolName=.comment : BSF_Local 00000000 00000000
SymbolName=./barebones_riscv_O3/startup.o : BSF_Local 00000000 00000000
SymbolName=_start : BSF_Local 00000100 00000100
SymbolName=format_regs : BSF_Local 00000100 00000110
SymbolName=loop : BSF_Local 00000100 00000190
SymbolName=core_list_join.c : BSF_Local 00000000 00000000
SymbolName=core_main.c : BSF_Local 00000000 00000000
SymbolName=list_known_crc : BSF_Local 80002c08 80002c08
SymbolName=matrix_known_crc : BSF_Local 80002c08 80002c14
SymbolName=state_known_crc : BSF_Local 80002c08 80002c20
SymbolName=core_matrix.c : BSF_Local 00000000 00000000
SymbolName=core_state.c : BSF_Local 00000000 00000000
SymbolName=intpat : BSF_Local 80002c08 80002c60
SymbolName=floatpat : BSF_Local 80002c08 80002c70
SymbolName=scipat : BSF_Local 80002c08 80002c80
SymbolName=errpat : BSF_Local 80002c08 80002c90
SymbolName=core_util.c : BSF_Local 00000000 00000000
SymbolName=core_portme.c : BSF_Local 00000000 00000000
SymbolName=start_time_val : BSF_Local 7f0007e8 7f0007ec
SymbolName=stop_time_val : BSF_Local 7f0007e8 7f0007e8
SymbolName=ee_printf.c : BSF_Local 00000000 00000000
SymbolName=number : BSF_Function 80000000 80001610
SymbolName=seed1_volatile : BSF_global 7f0007e8 7f0007f8
SymbolName=core_list_init : BSF_Function 80000000 800006bc
SymbolName=crcu32 : BSF_Function 80000000 80001568
SymbolName=check_data_types : BSF_Function 80000000 800015a8
SymbolName=stop_time : BSF_Function 80000000 800015c8
SymbolName=mem_name : BSF_global 7f000000 7f000000
SymbolName=core_list_reverse : BSF_Function 80000000 800002e8
SymbolName=crcu16 : BSF_Function 80000000 80001498
SymbolName=matrix_sum : BSF_Function 80000000 800009e4
SymbolName=portable_fini : BSF_Function 80000000 80001608
SymbolName=_gp : BSF_global 7f000000 7f000010
SymbolName=matrix_test : BSF_Function 80000000 80000c0c
SymbolName=get_time : BSF_Function 80000000 800015dc
SymbolName=core_bench_state : BSF_Function 80000000 8000120c
SymbolName=core_bench_list : BSF_Function 80000000 80000454
SymbolName=matrix_mul_const : BSF_Function 80000000 80000a64
SymbolName=ee_printf : BSF_Function 80000000 80001864
SymbolName=seed3_volatile : BSF_global 7f00000c 7f000014
SymbolName=seed4_volatile : BSF_global 7f00000c 7f000010
SymbolName=iterate : BSF_Function 80000000 8000081c
SymbolName=matrix_mul_matrix : BSF_Function 80000000 80000b28
SymbolName=cmp_complex : BSF_Function 80000000 80000164
SymbolName=core_state_transition : BSF_Function 80000000 80000fdc
SymbolName=core_bench_matrix : BSF_Function 80000000 80000e78
SymbolName=core_list_mergesort : BSF_Function 80000000 8000030c
SymbolName=start_time : BSF_Function 80000000 800015b8
SymbolName=seed5_volatile : BSF_global 7f0007e8 7f0007f0
SymbolName=_sp : BSF_global 00000000 7f004008
SymbolName=core_list_remove : BSF_Function 80000000 8000022c
SymbolName=crc16 : BSF_Function 80000000 8000159c
SymbolName=copy_info : BSF_Function 80000000 800001b4
SymbolName=core_list_undo_remove : BSF_Function 80000000 80000258
SymbolName=static_memblk : BSF_global 7f000018 7f000018
SymbolName=main : BSF_Function 80002034 80002034
SymbolName=barebones_clock : BSF_Function 80000000 800015b0
SymbolName=get_seed_32 : BSF_Function 80000000 800013e0
SymbolName=crcu8 : BSF_Function 80000000 80001430
SymbolName=matrix_mul_matrix_bitextract : BSF_Function 80000000 80000b90
SymbolName=core_list_insert_new : BSF_Function 80000000 800001c8
SymbolName=portable_init : BSF_Function 80000000 800015fc
SymbolName=core_init_matrix : BSF_Function 80000000 800008b0
SymbolName=matrix_mul_vect : BSF_Function 80000000 80000adc
SymbolName=matrix_add_const : BSF_Function 80000000 80000aa4
SymbolName=core_init_state : BSF_Function 80000000 80000eb8
SymbolName=time_in_secs : BSF_Function 80000000 800015ec
SymbolName=uart_send_char : BSF_Function 80000000 80001860
SymbolName=seed2_volatile : BSF_global 7f0007e8 7f0007f4
SymbolName=core_list_find : BSF_Function 80000000 80000278
SymbolName=cmp_idx : BSF_Function 80000000 80000000
SymbolName=calc_func : BSF_Function 80000000 8000005c
SymbolName=default_num_contexts : BSF_global 7f00000c 7f00000c

無事に、シンボル名、シンボルタイプ、グローバル変数、関数名の場合はアドレスが出力されている。これを拡張する形で、シミュレータに組み込んでいこう。