RustでELFファイルを開く続き。次はProgram Headerを読むコードを書いていく。Program Headerはセグメント領域の情報が格納されている。セグメントにはコードセグメントとデータセグメントが存在し、コードセグメントにはプログラム、データセグメントにはデータが格納されている。
前回のヘッダファイルの読み取りを見直してみると、
E_PHOFF = 40 // プログラムヘッダのファイル先頭からのオフセット E_SHOFF = 2270 // セグメントヘッダのファイル先頭からのオフセット E_FLAGS = 0 E_EHSIZE = 64 E_PHENTSIZE = 56 // プログラムヘッダのサイズ E_PHNUM = 2 // プログラムヘッダのエントリ数 E_SHENTSIZE = 64 // セグメントヘッダのサイズ E_SHNUM = 6 // セグメントヘッダのエントリ数 E_SHSTRNDX = 5
まずはプログラムヘッダ。プログラムヘッダのサイズは56バイトで、2つのエントリが格納されている。オフセットは0x40から始まっているので、ここからデータを取り出して構造体を作成する。
fn get_program_header(&self, idx: u32) -> ProgramHeader { let ph_off = self.get_e_phoff(); let ph_size = self.get_e_phentsize() as u32; let p_type = self.get_4byte_elf((ph_off + ph_size*idx ) as usize); let p_flags = self.get_4byte_elf((ph_off + ph_size*idx + 4 ) as usize); let p_offset = self.get_8byte_elf((ph_off + ph_size*idx + 4 + 4 ) as usize); let p_vaddr = self.get_8byte_elf((ph_off + ph_size*idx + 4 + 4 + 8 ) as usize); let p_paddr = self.get_8byte_elf((ph_off + ph_size*idx + 4 + 4 + 8 + 8 ) as usize); let p_filesz = self.get_8byte_elf((ph_off + ph_size*idx + 4 + 4 + 8 + 8 + 8 ) as usize); let p_memsz = self.get_8byte_elf((ph_off + ph_size*idx + 4 + 4 + 8 + 8 + 8 + 8 ) as usize); let p_align = self.get_8byte_elf((ph_off + ph_size*idx + 4 + 4 + 8 + 8 + 8 + 8 + 8 ) as usize); let phdr_type = match Phdr_Type::from_u64(p_type as u64) { Some(phdr_type) => phdr_type, None => panic!("Unknown Phdr Type"), }; ProgramHeader::new (phdr_type, p_flags, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz, p_align)
かなり無理やりだが、PHOFF
の先頭から順番にデータを取り出してProgramHeader
構造体に格納する。これをダンプすることで情報を表示する。
pub fn dump_phdr(&self) { println!(" Entry Type : {}", self.get_type_string()); println!(" Flags : {:x}", self.p_flags); println!(" Offset : {:x}", self.p_offset); println!(" VAddr : {:x}", self.p_vaddr); println!(" PAddr : {:x}", self.p_paddr); println!(" File Size : {:x}", self.p_filesz); println!(" Memory Size : {:x}", self.p_memsz); println!(" Alignment : {:x}", self.p_align); }
さらに、このセクション情報をもとに各セクションの内容をダンプすることにした。self.p_offset
とself.p_memsz
の間どデータをひたすらダンプする。
fn dump_section(&self, start: u64, memsz: u64) { for byte_idx in (start..(start + memsz)).step_by(4) { print!("{:08x} ", self.get_4byte_elf(byte_idx as usize)); if byte_idx % 16 == 16-4 { print!("\n"); } } print!("\n"); }
これで、以下のようなELFの情報を抽出できるようになった。
E_TYPE = ET_EXEC E_MACHINE = RISCV E_VERSION = 1 E_ENTRY = 80000000 E_PHOFF = 40 E_SHOFF = 2270 E_FLAGS = 0 E_EHSIZE = 64 E_PHENTSIZE = 56 E_PHNUM = 2 E_SHENTSIZE = 64 E_SHNUM = 6 E_SHSTRNDX = 5 Entry Type : PT_LOAD Flags : 5 Offset : 1000 VAddr : 80000000 PAddr : 80000000 File Size : 144 Memory Size : 144 Alignment : 1000 04c0006f 34202f73 00800f93 03ff0a63 00900f93 03ff0663 00b00f93 03ff0263 80000f17 fe0f0f13 000f0463 000f0067 34202f73 000f5463 0040006f 5391e193 00001f17 fc3f2023 ff9ff06f f1402573 00051063 00000297 01028293 30529073 18005073 00000297 01c28293 30529073 fff00293 3b029073 01f00293 3a029073 00000297 01828293 30529073 30205073 30305073 30405073 00000193 00000297 f6828293 30529073 00100513 01f51513 00055863 0ff0000f 00100193 00000073 80000297 f4028293 00028e63 10529073 0000b2b7 1092829b 30229073 30202373 f4629ee3 30005073 00000297 01428293 34129073 f1402573 30200073 0ff0000f ...