rust
DWARF
RustDay 20

gimli-rs - 死は確実にして業は成り難し。何を迷う?

冒頭のやつは指輪物語ですね、これがやりたかっただけです。

gimli-rsというDWARFを扱うためのライブラリをちょこっと紹介します。

DWARFというのはデバッグ情報のフォーマットのとこで、 詳しいことは デバッグ情報の歩き方 というすばらしいエントリがあるのでこっちみてください。DWARFをいい感じに操作したいとなると今までは選択肢はlibdwarfとか使ってCでやるしかなかったんですが、Rustでいい感じのやつが出てきたので選択肢が増えました。

Gimliは,

  • ゼロコピー
  • 遅延評価(.debug_infoとかがいっぱいDIEもってるとめっちゃでかいので嬉しいやつ)
  • クロスプラットフォーム(とりあえずELF/Mach-O、もしかしたらPEも使えるかもしれない)

といった感じで謳っています。

でまあ利用例だと

とかあって、俺がわざわざ雑なの書くよりこのへんのcrate読んだほうがいいですね。。dwprodとか地味に便利(ほんとか?)で、これまで readelf -wi hello| grep DW_AT_producer とかやってたのがいい感じになりました。

ちょっと触ってみる

なにもコード書かないのも運営に怒られそうなので(?)、しょぼいdwprodもどきを書いてどんな感じかみてみましょう。

こんなかんじのコードを書いてみます。

extern crate fallible_iterator;
extern crate gimli;
extern crate failure;

use std::fs::File;
use std::io::Read;

use failure::Error;

fn get_section(section_name: &str) -> Result<Vec<u8>, Error> {
    let mut file = File::open(section_name)?;
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer)?;
    Ok(buffer)
}

fn run() -> Result<(), Error> {
    let endian = gimli::LittleEndian;

    let info_dump = get_section(".debug_info").unwrap();
    let debug_info = gimli::DebugInfo::new(&info_dump, endian);
    let abbrev_dump = get_section(".debug_abbrev").unwrap();
    let debug_abbrev = gimli::DebugAbbrev::new(&abbrev_dump, endian);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = try!(unit.abbreviations(&debug_abbrev));

        let mut entries = unit.entries(&abbrevs);
        while let Some((_, entry)) = try!(entries.next_dfs()) {
            let mut attrs = entry.attrs();
            while let Some(attr) = attrs.next().unwrap() {
                if attr.name() == gimli::DW_AT_producer {
                    println!("Found a producer: {:?}", attr.value());
                    return Ok(()); // いっぱい出てくるから手抜きしてさっさと返しちゃいます
                }
            }
        }
    }
    Ok(())
}

fn main() {
    match run() {
        Ok(()) => {},
        Err(err) => println!("error: {}", err)
    }
}

てきとーにobjcopy使ってdebuginfoとdebugabbrevをdumpしておきましょう。

$ objcopy --dump-section .debug_abbrev=.debug_abbrev target/debug/producer
$ objcopy --dump-section .debug_info=.debug_info target/debug/producer

実行するとこんな感じのよくわからないのが出てきます。

$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/producer`
Found a producer: DebugStrRef(DebugStrOffset(0))

これは .debug_str セクションというまた別のセクションへの参照とオフセット情報を返しているので、場所を教えているだけです。ここから .debug_str を探してちゃんとやるとdwprodみたいな感じにできるはず…たぶん。

そういえばdmdとか-gつけても .debug_str 吐かないやついるんですけど地味に困りますね。

こんなところでお終いです。みなさんもgimliを使っていろいろやっていきましょう。