FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

RustでELFファイルを開く方法を調査する (2. Program Headerを読み取る)

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_offsetself.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
...