RustでELFを解析してダンプするプログラムを書いている。せっかくELFの中身をダンプできるようになったので、次はディスアセンブルを表示してみたい。ELFの中身をデコードできるようになれば、最終目的であるエミュレータまでの道が見えてくる。まずは依然作ったRISC-Vシミュレータのデコーダを拝借してきた。本当は個のデコーダは自動生成なのだけれども、今回は生成されたRustコードだけを拝借してきた。
impl RiscvDecoder { pub fn decode_inst (inst: u32) -> Option<RiscvInstId> { let field_op = ((inst as u64) >> 2) & (((1 as u64) << 5) - 1); return match field_op { 0x0d => Some(RiscvInstId::LUI), 0x05 => Some(RiscvInstId::AUIPC), 0x1b => Some(RiscvInstId::JAL), ...
オペランドの定義なども自動生成しているのでこれらのRustコードもコピーしてくる。
pub fn format_operand(&mut self) { { let mut inst_operand = OperandInfo::new(); // InstId_t::INST_ID_LUI inst_operand.m_size = 2; inst_operand.m_type_lst[0] = OperandType::TypeXReg; inst_operand.m_msb_lst[0] = 11; inst_operand.m_lsb_lst[0] = 7; inst_operand.m_connect[0] = false; // ["r[11:7]", "h[31:12]"] inst_operand.m_type_lst[1] = OperandType::TypeHex; inst_operand.m_msb_lst[1] = 31; inst_operand.m_lsb_lst[1] = 12; inst_operand.m_connect[1] = false; // ["r[11:7]", "h[31:12]"] self.m_inst_operand_map.insert(RiscvInstId::LUI, inst_operand); } ...
ニーモニックの定義も行う。テンプレートを定義して、@
のところをオペランドで置き換えるルーチンを定義する。
pub fn get_riscv_inst_mnemonic(dec_inst: RiscvInstId) -> String { match dec_inst { RiscvInstId::LUI => format!("lui @,@"), RiscvInstId::AUIPC => format!("auipc @,@"), RiscvInstId::JAL => format!("jal @,@"), RiscvInstId::JALR => format!("jalr @,@,@"), RiscvInstId::BEQ => format!("beq @,@,@<<1"), ...
dump_section()
が実際にダンプを生成するコードだ。これもシミュレータの実装から拝借してきた。
fn dump_section(&self, tracer: &Tracer, start: u64, memsz: u64) { for byte_idx in (start..(start + memsz)).step_by(4) { let inst_hex: u32 = self.get_4byte_elf(byte_idx as usize); print!("{:08x} ", inst_hex); let inst_decode = RiscvDecoder::decode_inst(inst_hex); ... match operand_info.m_type_lst[at_index] { OperandType::TypeXReg => { print!("x{:}", opr_val); consume_idx = consume_idx + 3; }, OperandType::TypeFreg => { print!("f{:}", opr_val); consume_idx = consume_idx + 3; }, OperandType::TypeSign => { print!("{:}", opr_val); consume_idx = consume_idx + ((opr_val as f32).log10() as u32); }, OperandType::TypeBit => { print!("0b{:b}", opr_val); consume_idx = consume_idx + (max_msb - min_lsb + 1); }, OperandType::TypeUnSign => { print!("0x{:x}", opr_val); consume_idx = consume_idx + 2 + ((opr_val as f32).log10() as u32); }, OperandType::TypeUnSignJ => { print!("0x{:x}", opr_val); consume_idx = consume_idx + 2 + ((opr_val as f32).log10() as u32); },
これらの実装で、ELFファイルを読み込んで逆アセンブルを出力することができるようになった。