Qualcomm Snapdragon (MSM) bootloader.img format: Perl parsing library

A bootloader for Android used on various devices powered by Qualcomm Snapdragon chips:

https://en.wikipedia.org/wiki/Devices_using_Qualcomm_Snapdragon_processors

Although not all of the Snapdragon based Android devices use this particular bootloader format, it is known that devices with the following chips have used it (example devices are given for each chip):

(*) bootloader_size is equal to the size of the whole file (not just img_bodies as usual).

(**) There are some data after the end of img_bodies.


On the other hand, devices with these chips do not use this format:


The bootloader-*.img samples referenced above originally come from factory images packed in ZIP archives that can be found on the page Factory Images for Nexus and Pixel Devices on the Google Developers site. Note that the codenames on that page may be different than the ones that are written in the list above. That's because the Google page indicates ROM codenames in headings (e.g. "occam" for Nexus 4) but the above list uses model codenames (e.g. "mako" for Nexus 4) because that is how the original bootloader-*.img files are identified. For most devices, however, these code names are the same.

File extension

img

KS implementation details

License: CC0-1.0

This page hosts a formal specification of Qualcomm Snapdragon (MSM) bootloader.img format using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Perl source code to parse Qualcomm Snapdragon (MSM) bootloader.img format

AndroidBootldrQcom.pm

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

use strict;
use warnings;
use IO::KaitaiStruct 0.009_000;
use Encode;

########################################################################
package AndroidBootldrQcom;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{magic} = $self->{_io}->read_bytes(8);
    $self->{num_images} = $self->{_io}->read_u4le();
    $self->{ofs_img_bodies} = $self->{_io}->read_u4le();
    $self->{bootloader_size} = $self->{_io}->read_u4le();
    $self->{img_headers} = ();
    my $n_img_headers = $self->num_images();
    for (my $i = 0; $i < $n_img_headers; $i++) {
        push @{$self->{img_headers}}, AndroidBootldrQcom::ImgHeader->new($self->{_io}, $self, $self->{_root});
    }
}

sub img_bodies {
    my ($self) = @_;
    return $self->{img_bodies} if ($self->{img_bodies});
    my $_pos = $self->{_io}->pos();
    $self->{_io}->seek($self->ofs_img_bodies());
    $self->{img_bodies} = ();
    my $n_img_bodies = $self->num_images();
    for (my $i = 0; $i < $n_img_bodies; $i++) {
        push @{$self->{img_bodies}}, AndroidBootldrQcom::ImgBody->new($self->{_io}, $self, $self->{_root});
    }
    $self->{_io}->seek($_pos);
    return $self->{img_bodies};
}

sub magic {
    my ($self) = @_;
    return $self->{magic};
}

sub num_images {
    my ($self) = @_;
    return $self->{num_images};
}

sub ofs_img_bodies {
    my ($self) = @_;
    return $self->{ofs_img_bodies};
}

sub bootloader_size {
    my ($self) = @_;
    return $self->{bootloader_size};
}

sub img_headers {
    my ($self) = @_;
    return $self->{img_headers};
}

########################################################################
package AndroidBootldrQcom::ImgHeader;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{name} = Encode::decode("ASCII", IO::KaitaiStruct::Stream::bytes_terminate($self->{_io}->read_bytes(64), 0, 0));
    $self->{len_body} = $self->{_io}->read_u4le();
}

sub name {
    my ($self) = @_;
    return $self->{name};
}

sub len_body {
    my ($self) = @_;
    return $self->{len_body};
}

########################################################################
package AndroidBootldrQcom::ImgBody;

our @ISA = 'IO::KaitaiStruct::Struct';

sub from_file {
    my ($class, $filename) = @_;
    my $fd;

    open($fd, '<', $filename) or return undef;
    binmode($fd);
    return new($class, IO::KaitaiStruct::Stream->new($fd));
}

sub new {
    my ($class, $_io, $_parent, $_root) = @_;
    my $self = IO::KaitaiStruct::Struct->new($_io);

    bless $self, $class;
    $self->{_parent} = $_parent;
    $self->{_root} = $_root || $self;;

    $self->_read();

    return $self;
}

sub _read {
    my ($self) = @_;

    $self->{body} = $self->{_io}->read_bytes($self->img_header()->len_body());
}

sub img_header {
    my ($self) = @_;
    return $self->{img_header} if ($self->{img_header});
    $self->{img_header} = @{$self->_root()->img_headers()}[$self->idx()];
    return $self->{img_header};
}

sub body {
    my ($self) = @_;
    return $self->{body};
}

sub idx {
    my ($self) = @_;
    return $self->{idx};
}

1;