Wątek przeniesiony 2021-08-05 08:04 z Inne języki programowania przez Adam Boduch.

Rust - konwersja bajtów na struktruę

1

Cześć, uczę się rusta i stwierdziłem, że spróbuję parsowania pliku PE.
Przy parsowaniu nagłówku DOS nie było problemu, natomiast przy nagłówku COFF, pojawił się rust ma problem z nałożeniem tablicy bajtów na strukturę. Nie rozumiem, dlaczego wcześniej zadziałało, a teraz jest z tym problem.

#[derive(Debug, Copy, Clone, Default)]
struct COFFHeader
{
    Machine: u16,               // 2
    NumberOfSections: u16,      // 2
    TimeDateStamp: u32,         // 4
    PointerToSymbolTable: u32,  // 4
    NumberOfSymbols: u32,       // 4
    SizeOfOptionalHeader: u16,  // 2
    Characteristics: u16,       // 2
                                // 20
}
fn test()  -> COFFHeader{
    let x:[u8;20] = [100, 134, 5, 0, 101, 201, 230, 96, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 34, 0];
    let ptr = x.as_ptr() as *const COFFHeader;
    return unsafe {ptr.read()};
}

Niestety, ale np pole Machine wynosi zero, a powinno 0x8664

Jednak jak spróbuje skonwertować pierwsze dwa bajty na u16

fn test2() -> u16 {
    let x:[u8;20] = [100, 134, 5, 0, 101, 201, 230, 96, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 34, 0];
    let ptr = x.as_ptr() as *const u16;
    return unsafe {ptr.read()};
}

Dostaję wyniku poprawny wynik 0x8664

Nie mam pojęcia czemu to nie chce poprawnie zadziałać, tym bardziej, że przy wcześniejszym nakładaniu bajtów na strukturę nagłówka DOS, odbyło się to poprawnie.
Być może są lepsze sposoby na tworzenie struktury z tablicy bajtów?

2

Nie znam sie na ruście, ale nie ma czasem jakiegoś alignmentu pól struktury i żeby pola przylegały tak jak zadeklarujesz (bez jakichś paddingów) to musisz jakoś to zadeklarować? W C potrzeba użyć specjalnego atrybutu packed żeby kompilator nie dodał paddingów.

5

To ci się może przydać, piszą o tym, jak są struktury rusta trzymane w pamięci: https://doc.rust-lang.org/reference/type-layout.html#representations
Swoją drogą piszą, że (w domyślnym layoucie): "There are no guarantees of data layout made by this representation.".
Może warto spróbować z #[repr(C)]?
https://doc.rust-lang.org/reference/type-layout.html#reprc-structs

1

@LukeJL: Tak dokładnie tego zabrakło, dzięki :)

3

Btw, jeśli szukasz czegoś idiomatycznego, rzuć okiem na https://github.com/bincode-org/bincode.

2

Przy czym powinieneś użyć rozwiązania @Patryk27 z bincode, które jest bezpieczniejsze i ogólnie jest zalecanym rozwiązaniem. Surowe rzutowanie wskaźników jest czymś czego powinno się unikać w Ruscie.

4

To nie zadziała. Kompilator ma dowolność w układaniu pól struktury. Może zmienić kolejność pól, może dodać padding (również w środku). No i skąd pewność, że bajty np. takiego inta nie są w odwrotnej kolejności (little vs big endian)? To co robisz, to undefined behaviour.

Generalnie zasada jest taka, że jeśli dogłębnie nie rozumiesz co robisz, to nie używaj unsafe.

Zamiast unsafe użyj normalnie metod traita Read i czytaj po kolei.

1 użytkowników online, w tym zalogowanych: 0, gości: 1