m4b / goblin

An impish, cross-platform binary parsing crate, written in Rust
MIT License
1.19k stars 158 forks source link

Failing to parse section headers of MIPS LE 32-bit ELF correctly on 64-bit PPC BE host #420

Open sajattack opened 2 months ago

sajattack commented 2 months ago

Hello, I seem to have stumbled upon some endian-awareness issues, unless you think I'm somehow misusing the library.

I have this goblin program (simplified from my actual usecase for ease of troubleshooting)

use std::fs;
use goblin::elf32::{header::Header, section_header::SectionHeader, section_header::SHT_REL};

fn main() {
    let elf_bytes = fs::read("psp-cube-example").unwrap();
    let header = Header::parse(&elf_bytes).unwrap();

    let section_headers = SectionHeader::from_bytes(
        &elf_bytes[header.e_shoff as usize..],
        header.e_shnum as usize,
    );

    let reloc_count = section_headers.iter().filter(|sh| sh.sh_type == SHT_REL).count();
    println!("reloc_count: {}", reloc_count);
    println!("section_headers: {:?}", section_headers);
}

and I have this 32-bit MIPS2 LE ELF file (uploaded as zip because github is dumb) psp-cube-example.zip

When I run this program on x86_64 Little Endian - I get the correct result. goblin_repro_x86_64.txt

When I run this program on 64-bit PowerPC - I do not. goblin_repro_ppc64.txt

I am using the default endian_fd feature of goblin.

Here is the output of readelf -S

paul@ps3:~/goblin-repro$ readelf -S psp-cube-example
There are 34 section headers, starting at offset 0x46d44:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        00000000 000120 00af20 00  AX  0   0 16
  [ 2] .sceStub.text     PROGBITS        0000af20 00b040 0000b8 00   A  0   0  4
  [ 3] .lib.stub.top     PROGBITS        0000afd8 00b0f8 000004 00   A  0   0  4
  [ 4] .lib.stub         PROGBITS        0000afdc 00b0fc 000078 00   A  0   0  4
  [ 5] .lib.stub.btm     PROGBITS        0000b054 00b174 000004 00   A  0   0  4
  [ 6] .lib.ent.top      PROGBITS        0000b058 00b178 000004 00   A  0   0  4
  [ 7] .lib.ent          PROGBITS        0000b05c 00b17c 000010 00   A  0   0  1
  [ 8] .lib.ent.btm      PROGBITS        0000b06c 00b18c 000004 00   A  0   0  4
  [ 9] .eh_frame_hdr     PROGBITS        0000b070 00b190 0005cc 00   A  0   0  4
  [10] .eh_frame         PROGBITS        0000b63c 00b75c 0014d4 00   A  0   0  4
  [11] .rodata.sceR[...] PROGBITS        0000cb10 00cc30 000074 00   A  0   0  4
  [12] .rodata.sceM[...] PROGBITS        0000cb90 00ccb0 000040 00   A  0   0 16
  [13] .rodata.sceNid    PROGBITS        0000cbd0 00ccf0 00005c 00   A  0   0  4
  [14] .rodata           PROGBITS        0000cc30 00cd50 011e22 00 AMS  0   0 16
  [15] .data             PROGBITS        0001ea54 01eb74 000068 00  WA  0   0  4
  [16] .got              PROGBITS        0001eac0 01ebe0 000008 00 WAp  0   0 16
  [17] .gcc_except_table PROGBITS        0001eac8 01ebe8 000dd8 00   A  0   0  4
  [18] .bss              NOBITS          0001f8a0 01f9c0 1030a4 00  WA  0   0 16
  [19] .mdebug.abi32     PROGBITS        00000000 01f9c0 000000 00      0   0  1
  [20] .rel.text         REL             00000000 01f9c0 002f68 08   I 31   1  4
  [21] .pdr              PROGBITS        00000000 022928 0151a0 00      0   0  4
  [22] .rel.pdr          REL             00000000 037ac8 005468 08   I 31  21  4
  [23] .rel.rodata       REL             00000000 03cf30 000f78 08   I 31  14  4
  [24] .rel.rodata.[...] REL             00000000 03dea8 000028 08   I 31  12  4
  [25] .rel.lib.ent      REL             00000000 03ded0 000008 08   I 31   7  4
  [26] .rel.rodata.[...] REL             00000000 03ded8 000010 08   I 31  11  4
  [27] .rel.data         REL             00000000 03dee8 000048 08   I 31  15  4
  [28] .comment          PROGBITS        00000000 03df30 0000bf 01  MS  0   0  1
  [29] .rel.lib.stub     REL             00000000 03dff0 000090 08   I 31   4  4
  [30] .rel.sceStub.text REL             00000000 03e080 000170 08   I 31   2  4
  [31] .symtab           SYMTAB          00000000 03e1f0 002c20 10     33 504  4
  [32] .shstrtab         STRTAB          00000000 040e10 000197 00      0   0  1
  [33] .strtab           STRTAB          00000000 040fa7 005d9b 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), p (processor specific)

Let me know if there's any other details you need, I hope you may be able to reproduce using qemu-system-ppc64, but I have not explored it myself.

For extra background on what I'm actually doing, because it's kind of fun :smile: I'm trying to use rust-psp to compile a psp homebrew app on a ps3 running linux. And I hit this assertion in our psp executable repacker: https://github.com/overdrivenpotato/rust-psp/blob/bfaa487ea4881395cc64fdd82158745885222a29/cargo-psp/src/bin/prxgen.rs#L98

sajattack commented 2 months ago

It looks like I should be using from_fd instead of from_bytes

sajattack commented 2 months ago

Reopening, from_fd doesn't work either.

use std::{fs::File, io::{Seek, SeekFrom}};
use goblin::elf32::{header::Header, section_header::SectionHeader, section_header::SHT_REL};

fn main() {
    let mut fd = File::open("psp-cube-example").unwrap();

    let header = Header::from_fd(&mut fd).unwrap();

    println!("header: {:?}" , header);

    //fd.rewind().unwrap();
    let section_headers = SectionHeader::from_fd(
        &mut fd,
        header.e_shoff.into(),
        header.e_shnum.into(),
    ).unwrap();

    let reloc_count = section_headers.iter().filter(|sh| sh.sh_type == SHT_REL).count();
    println!("reloc_count: {}", reloc_count);
    println!("section_headers: {:?}", section_headers);
}
    Running `target/debug/goblin-repro`
header: Header { e_ident: [127, 69, 76, 70, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0], e_type: "UNKNOWN_ET", e_machine: 0x800, e_version: 0x1000000, e_entry: 0x10040000, e_phoff: 0x34000000, e_shoff: 0x446d0400, e_flags: 5100010, e_ehsize: 13312, e_phentsize: 8192, e_phnum: 1792, e_shentsize: 10240, e_shnum: 8704, e_shstrndx: 8192 }
thread 'main' panicked at src/main.rs:16:7:
called `Result::unwrap()` on an `Err` value: IO(Error { kind: UnexpectedEof, message: "failed to fill whole buffer" })
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Actual values:

[0x00000410]> iH
0x00000000  ELF MAGIC   0x464c457f
0x00000010  Type        0x0002
0x00000012  Machine     0x0008
0x00000014  Version     0x00000001
0x00000018  Entrypoint  0x00000410
0x0000001c  PhOff       0x00000034
0x00000020  ShOff       0x00046d44
0x00000024  Flags       0x10001005
0x00000028  EhSize      52
0x0000002a  PhentSize   32
0x0000002c  PhNum       7
0x0000002e  ShentSize   40
0x00000030  ShNum       34
0x00000032  ShrStrndx   32