冒頭のやつは指輪物語ですね、これがやりたかっただけです。
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を使っていろいろやっていきましょう。