Android DTB/DTBO Partition: C++11/STL parsing library

Format for Android DTB/DTBO partitions. It's kind of archive with dtb/dtbo files. Used only when there is a separate unique partition (dtb, dtbo) on an android device to organize device tree files. The format consists of a header with info about size and number of device tree entries and the entries themselves. This format description could be used to extract device tree entries from a partition images and decompile them with dtc (device tree compiler).

File extension

img

KS implementation details

License: CC0-1.0

This page hosts a formal specification of Android DTB/DTBO Partition using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Runtime library

All parsing code for C++11/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.

For C++, the easiest way is to clone the runtime library sources and build them along with your project.

Code

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (std::istream). One can open local file for that, or use existing std::string or char* buffer.
    #include <fstream>
    
    std::ifstream is("path/to/local/file.img", std::ifstream::binary);
    
    #include <sstream>
    
    std::istringstream is(str);
    
    #include <sstream>
    
    const char buf[] = { ... };
    std::string str(buf, sizeof buf);
    std::istringstream is(str);
    
  2. We need to wrap our input stream into Kaitai stream:
    #include "kaitai/kaitaistream.h"
    
    kaitai::kstream ks(&is);
    
  3. And finally, we can invoke the parsing:
    android_dto_t data(&ks);
    

After that, one can get various attributes from the structure by invoking getter methods like:

data.header() // => get header

C++11/STL source code to parse Android DTB/DTBO Partition

android_dto.h

#pragma once

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <memory>
#include <vector>

#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif

/**
 * Format for Android DTB/DTBO partitions. It's kind of archive with
 * dtb/dtbo files. Used only when there is a separate unique partition
 * (dtb, dtbo) on an android device to organize device tree files.
 * The format consists of a header with info about size and number
 * of device tree entries and the entries themselves. This format
 * description could be used to extract device tree entries from a
 * partition images and decompile them with dtc (device tree compiler).
 * \sa https://source.android.com/docs/core/architecture/dto/partitions Source
 * \sa https://android.googlesource.com/platform/system/libufdt/+/refs/tags/android-10.0.0_r47 Source
 */

class android_dto_t : public kaitai::kstruct {

public:
    class dt_table_header_t;
    class dt_table_entry_t;

    android_dto_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, android_dto_t* p__root = nullptr);

private:
    void _read();
    void _clean_up();

public:
    ~android_dto_t();

    class dt_table_header_t : public kaitai::kstruct {

    public:

        dt_table_header_t(kaitai::kstream* p__io, android_dto_t* p__parent = nullptr, android_dto_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~dt_table_header_t();

    private:
        std::string m_magic;
        uint32_t m_total_size;
        uint32_t m_header_size;
        uint32_t m_dt_entry_size;
        uint32_t m_dt_entry_count;
        uint32_t m_dt_entries_offset;
        uint32_t m_page_size;
        uint32_t m_version;
        android_dto_t* m__root;
        android_dto_t* m__parent;

    public:
        std::string magic() const { return m_magic; }

        /**
         * includes dt_table_header + all dt_table_entry and all dtb/dtbo
         */
        uint32_t total_size() const { return m_total_size; }

        /**
         * sizeof(dt_table_header)
         */
        uint32_t header_size() const { return m_header_size; }

        /**
         * sizeof(dt_table_entry)
         */
        uint32_t dt_entry_size() const { return m_dt_entry_size; }

        /**
         * number of dt_table_entry
         */
        uint32_t dt_entry_count() const { return m_dt_entry_count; }

        /**
         * offset to the first dt_table_entry from head of dt_table_header
         */
        uint32_t dt_entries_offset() const { return m_dt_entries_offset; }

        /**
         * flash page size
         */
        uint32_t page_size() const { return m_page_size; }

        /**
         * DTBO image version
         */
        uint32_t version() const { return m_version; }
        android_dto_t* _root() const { return m__root; }
        android_dto_t* _parent() const { return m__parent; }
    };

    class dt_table_entry_t : public kaitai::kstruct {

    public:

        dt_table_entry_t(kaitai::kstream* p__io, android_dto_t* p__parent = nullptr, android_dto_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~dt_table_entry_t();

    private:
        bool f_body;
        std::string m_body;

    public:

        /**
         * DTB/DTBO file
         */
        std::string body();

    private:
        uint32_t m_dt_size;
        uint32_t m_dt_offset;
        uint32_t m_id;
        uint32_t m_rev;
        std::unique_ptr<std::vector<uint32_t>> m_custom;
        android_dto_t* m__root;
        android_dto_t* m__parent;

    public:

        /**
         * size of this entry
         */
        uint32_t dt_size() const { return m_dt_size; }

        /**
         * offset from head of dt_table_header
         */
        uint32_t dt_offset() const { return m_dt_offset; }

        /**
         * optional, must be zero if unused
         */
        uint32_t id() const { return m_id; }

        /**
         * optional, must be zero if unused
         */
        uint32_t rev() const { return m_rev; }

        /**
         * optional, must be zero if unused
         */
        std::vector<uint32_t>* custom() const { return m_custom.get(); }
        android_dto_t* _root() const { return m__root; }
        android_dto_t* _parent() const { return m__parent; }
    };

private:
    std::unique_ptr<dt_table_header_t> m_header;
    std::unique_ptr<std::vector<std::unique_ptr<dt_table_entry_t>>> m_entries;
    android_dto_t* m__root;
    kaitai::kstruct* m__parent;

public:
    dt_table_header_t* header() const { return m_header.get(); }
    std::vector<std::unique_ptr<dt_table_entry_t>>* entries() const { return m_entries.get(); }
    android_dto_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};

android_dto.cpp

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "android_dto.h"
#include "kaitai/exceptions.h"

android_dto_t::android_dto_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, android_dto_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_header = nullptr;
    m_entries = nullptr;
    _read();
}

void android_dto_t::_read() {
    m_header = std::unique_ptr<dt_table_header_t>(new dt_table_header_t(m__io, this, m__root));
    m_entries = std::unique_ptr<std::vector<std::unique_ptr<dt_table_entry_t>>>(new std::vector<std::unique_ptr<dt_table_entry_t>>());
    const int l_entries = header()->dt_entry_count();
    for (int i = 0; i < l_entries; i++) {
        m_entries->push_back(std::move(std::unique_ptr<dt_table_entry_t>(new dt_table_entry_t(m__io, this, m__root))));
    }
}

android_dto_t::~android_dto_t() {
    _clean_up();
}

void android_dto_t::_clean_up() {
}

android_dto_t::dt_table_header_t::dt_table_header_t(kaitai::kstream* p__io, android_dto_t* p__parent, android_dto_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void android_dto_t::dt_table_header_t::_read() {
    m_magic = m__io->read_bytes(4);
    if (!(magic() == std::string("\xD7\xB7\xAB\x1E", 4))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\xD7\xB7\xAB\x1E", 4), magic(), _io(), std::string("/types/dt_table_header/seq/0"));
    }
    m_total_size = m__io->read_u4be();
    m_header_size = m__io->read_u4be();
    m_dt_entry_size = m__io->read_u4be();
    m_dt_entry_count = m__io->read_u4be();
    m_dt_entries_offset = m__io->read_u4be();
    m_page_size = m__io->read_u4be();
    m_version = m__io->read_u4be();
}

android_dto_t::dt_table_header_t::~dt_table_header_t() {
    _clean_up();
}

void android_dto_t::dt_table_header_t::_clean_up() {
}

android_dto_t::dt_table_entry_t::dt_table_entry_t(kaitai::kstream* p__io, android_dto_t* p__parent, android_dto_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_custom = nullptr;
    f_body = false;
    _read();
}

void android_dto_t::dt_table_entry_t::_read() {
    m_dt_size = m__io->read_u4be();
    m_dt_offset = m__io->read_u4be();
    m_id = m__io->read_u4be();
    m_rev = m__io->read_u4be();
    m_custom = std::unique_ptr<std::vector<uint32_t>>(new std::vector<uint32_t>());
    const int l_custom = 4;
    for (int i = 0; i < l_custom; i++) {
        m_custom->push_back(std::move(m__io->read_u4be()));
    }
}

android_dto_t::dt_table_entry_t::~dt_table_entry_t() {
    _clean_up();
}

void android_dto_t::dt_table_entry_t::_clean_up() {
    if (f_body) {
    }
}

std::string android_dto_t::dt_table_entry_t::body() {
    if (f_body)
        return m_body;
    kaitai::kstream *io = _root()->_io();
    std::streampos _pos = io->pos();
    io->seek(dt_offset());
    m_body = io->read_bytes(dt_size());
    io->seek(_pos);
    f_body = true;
    return m_body;
}