Add the Qualcomm Peripheral Image loader (MDT files, 32bit only).
It loads .mdt firmware layout files, searches for the other parts in the same directory and loads them. It resolves symbols, sections etc automatically, and maps them according to the layout to the virtual address space. - The iH command provides details about each firmware part as well. - Memory maps and segment names are prefixed with the parts file name to make them distinct. - If a firmware part is an ELF library it loads, and resolves its functions, segments etc. Due to the design of the ELF binary plugin (code duplication with macros for 32/64bit versions), this only supports 32bit firmware images for now. This can relatively easily be fixed, by duplicating this plugin but including the 64bit ELF headers. This requires more tests though.
This commit is contained in:
parent
28bd9cfa24
commit
1f6c6147ec
10 changed files with 1259 additions and 3 deletions
|
|
@ -7,12 +7,12 @@ coverage:
|
|||
- 'release-*'
|
||||
if_not_found: failure
|
||||
target: auto
|
||||
threshold: 0.1%
|
||||
threshold: 0.5%
|
||||
patch:
|
||||
default:
|
||||
if_not_found: success
|
||||
target: auto
|
||||
threshold: 0.1%
|
||||
threshold: 0.5%
|
||||
ignore:
|
||||
- "binrz/rz-test"
|
||||
- ".github"
|
||||
|
|
|
|||
|
|
@ -1120,6 +1120,35 @@ RZ_API void rz_bin_virtual_file_free(RZ_NULLABLE RzBinVirtualFile *vfile) {
|
|||
free(vfile);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Clones the virtual file. If the buffer associated with it is owned, it will also clone the buffer.
|
||||
* If it is not owned, it will copy the pointer.
|
||||
*
|
||||
* \param vfile The virtual file to clone.
|
||||
*
|
||||
* \return The virtual file clone or NULL in case of failure.
|
||||
*/
|
||||
RZ_API RZ_OWN RzBinVirtualFile *rz_bin_virtual_file_clone(RZ_BORROW RZ_NONNULL RzBinVirtualFile *vfile) {
|
||||
rz_return_val_if_fail(vfile, NULL);
|
||||
RzBinVirtualFile *clone = RZ_NEW0(RzBinVirtualFile);
|
||||
if (!clone) {
|
||||
return NULL;
|
||||
}
|
||||
clone->buf_owned = vfile->buf_owned;
|
||||
clone->buf = vfile->buf_owned ? rz_buf_new_with_buf(vfile->buf) : vfile->buf;
|
||||
if (!clone->buf) {
|
||||
return NULL;
|
||||
}
|
||||
clone->name = rz_str_dup(vfile->name);
|
||||
if (!clone->name) {
|
||||
if (clone->buf_owned) {
|
||||
rz_buf_free(clone->buf);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
RZ_API void rz_bin_map_free(RZ_NULLABLE RzBinMap *map) {
|
||||
if (!map) {
|
||||
return;
|
||||
|
|
@ -1129,6 +1158,25 @@ RZ_API void rz_bin_map_free(RZ_NULLABLE RzBinMap *map) {
|
|||
free(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Clones an RzBinMap.
|
||||
*
|
||||
* \param map The map to clone.
|
||||
*
|
||||
* \return The clone of \p map or NULL in case of failure.
|
||||
*/
|
||||
RZ_API RZ_OWN RzBinMap *rz_bin_map_clone(RZ_NONNULL RzBinMap *map) {
|
||||
rz_return_val_if_fail(map, NULL);
|
||||
RzBinMap *clone = RZ_NEW0(RzBinMap);
|
||||
if (!clone) {
|
||||
return NULL;
|
||||
}
|
||||
rz_mem_copy(clone, sizeof(RzBinMap), map, sizeof(RzBinMap));
|
||||
clone->name = rz_str_dup(map->name);
|
||||
clone->vfile_name = map->vfile_name ? rz_str_dup(map->vfile_name) : NULL;
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Create a pvector of RzBinMap from RzBinSections queried from the given file
|
||||
*
|
||||
|
|
|
|||
654
librz/bin/format/mdt/mdt.c
Normal file
654
librz/bin/format/mdt/mdt.c
Normal file
|
|
@ -0,0 +1,654 @@
|
|||
// SPDX-FileCopyrightText: 2025 Rot127 <unisono@quyllur.org>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include "mdt.h"
|
||||
#include "../elf/elf.h"
|
||||
#include "../elf/elf_parser.h"
|
||||
#include "rz_bin.h"
|
||||
#include "rz_util/rz_assert.h"
|
||||
#include "rz_util/rz_buf.h"
|
||||
#include "rz_util/rz_file.h"
|
||||
#include "rz_util/rz_itv.h"
|
||||
#include "rz_util/rz_str.h"
|
||||
#include <rz_vector.h>
|
||||
#include <rz_util.h>
|
||||
#include <rz_io.h>
|
||||
|
||||
static inline bool is_layout_bin(size_t p_flags) {
|
||||
return (p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_LAYOUT;
|
||||
}
|
||||
|
||||
RZ_IPI RzBinMdtPart *rz_bin_mdt_part_new(const char *name, size_t p_flags) {
|
||||
RzBinMdtPart *part = RZ_NEW0(RzBinMdtPart);
|
||||
if (!part) {
|
||||
return NULL;
|
||||
}
|
||||
part->name = rz_str_dup(name);
|
||||
part->relocatable = p_flags & QCOM_MDT_RELOCATABLE;
|
||||
part->is_layout = is_layout_bin(p_flags);
|
||||
return part;
|
||||
}
|
||||
|
||||
RZ_IPI void rz_bin_mdt_part_free(RZ_OWN RZ_NULLABLE RzBinMdtPart *part) {
|
||||
if (!part) {
|
||||
return;
|
||||
}
|
||||
rz_bin_virtual_file_free(part->vfile);
|
||||
switch (part->format) {
|
||||
default:
|
||||
break;
|
||||
case RZ_BIN_MDT_PART_ELF:
|
||||
Elf32_rz_bin_elf_free(part->obj.elf);
|
||||
break;
|
||||
case RZ_BIN_MDT_PART_MBN:
|
||||
mbn_destroy_obj(part->obj.mbn);
|
||||
break;
|
||||
}
|
||||
rz_bin_map_free(part->map);
|
||||
rz_pvector_free(part->relocs);
|
||||
rz_pvector_free(part->symbols);
|
||||
rz_pvector_free(part->sections);
|
||||
rz_pvector_free(part->sub_maps);
|
||||
free(part->patches_vfile_name);
|
||||
free(part->relocs_vfile_name);
|
||||
free(part->name);
|
||||
free(part);
|
||||
}
|
||||
|
||||
RZ_IPI RzBinMdtObj *rz_bin_mdt_obj_new() {
|
||||
RzBinMdtObj *obj = RZ_NEW0(RzBinMdtObj);
|
||||
if (!obj) {
|
||||
return NULL;
|
||||
}
|
||||
obj->parts = rz_pvector_new((RzPVectorFree)rz_bin_mdt_part_free);
|
||||
return obj;
|
||||
}
|
||||
|
||||
RZ_IPI void rz_bin_mdt_obj_free(RzBinMdtObj *obj) {
|
||||
if (!obj) {
|
||||
return;
|
||||
}
|
||||
Elf32_rz_bin_elf_free(obj->header);
|
||||
rz_pvector_free(obj->parts);
|
||||
free(obj->name);
|
||||
free(obj);
|
||||
}
|
||||
|
||||
static inline bool is_elf32(RzBuffer *b) {
|
||||
return elf_check_buffer_aux(b) == ELFCLASS32;
|
||||
}
|
||||
|
||||
RZ_IPI bool rz_bin_mdt_check_buffer(RzBuffer *b) {
|
||||
rz_return_val_if_fail(b, false);
|
||||
if (!is_elf32(b) || rz_buf_size(b) <= RZ_BIN_ELF_TINY_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ELFOBJ *elf = NULL;
|
||||
RzBinObjectLoadOptions obj_opts = {
|
||||
.baseaddr = 0,
|
||||
.loadaddr = 0,
|
||||
.elf_load_sections = false,
|
||||
};
|
||||
|
||||
elf = Elf32_rz_bin_elf_new_buf(b, &obj_opts);
|
||||
if (!elf || !elf->segments) {
|
||||
Elf32_rz_bin_elf_free(elf);
|
||||
return false;
|
||||
}
|
||||
RzBinElfSegment *sgmt = rz_vector_head(elf->segments);
|
||||
if (!sgmt) {
|
||||
Elf32_rz_bin_elf_free(elf);
|
||||
return false;
|
||||
}
|
||||
bool mdt_flags_set = is_layout_bin(sgmt->data.p_flags);
|
||||
Elf32_rz_bin_elf_free(elf);
|
||||
return mdt_flags_set;
|
||||
}
|
||||
|
||||
static bool load_unidentified_obj_data(RZ_OUT RzBinMdtPart *part, RZ_OWN RzBinElfSegment *segment, RZ_OWN RzBinVirtualFile *vfile, RZ_OWN RzBinMap *map) {
|
||||
rz_return_val_if_fail(part && segment && vfile && map, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool load_mbn_obj_data(RZ_OUT RzBinMdtPart *part, RZ_OWN RzBinElfSegment *segment, RZ_OWN RzBinVirtualFile *vfile, RZ_OWN RzBinMap *map) {
|
||||
rz_return_val_if_fail(part && segment && vfile && map, false);
|
||||
|
||||
SblHeader *mbn = RZ_NEW0(SblHeader);
|
||||
ut64 offset = 0;
|
||||
if (!mbn || !mbn_read_sbl_header(vfile->buf, mbn, &offset)) {
|
||||
mbn_destroy_obj(mbn);
|
||||
mbn = NULL;
|
||||
}
|
||||
part->obj.mbn = mbn;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void prefix_name(char **to_prefix, const char *prefix) {
|
||||
rz_return_if_fail(to_prefix && prefix);
|
||||
const char *separator = *to_prefix[0] == '.' ? "" : ".";
|
||||
char *prefixed_name = rz_str_newf("%s%s%s", prefix, separator, RZ_STR_ISEMPTY(*to_prefix) ? "0x0" : *to_prefix);
|
||||
free(*to_prefix);
|
||||
*to_prefix = prefixed_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Couldn't figure out why, but the ELF module is inconsistent with the
|
||||
* virtual base addresses. Sometimes it updates the symbols and relocs with the
|
||||
* base address passed via the load options, sometimes it doesn't.
|
||||
* It seems to depend if the binary has relocations.
|
||||
* But since fixing it is too much trouble, this workaround has to do it.
|
||||
*/
|
||||
static void normalize_vaddr_of_elf(ELFOBJ *elf, ut64 base_vaddr) {
|
||||
elf->baddr = base_vaddr;
|
||||
RzBinElfSegment *segment;
|
||||
rz_bin_elf_foreach_segments(elf, segment) {
|
||||
segment->data.p_vaddr += base_vaddr;
|
||||
}
|
||||
|
||||
RzBinElfSection *section;
|
||||
rz_bin_elf_foreach_sections(elf, section) {
|
||||
if (section->rva != UT64_MAX) {
|
||||
section->rva += base_vaddr;
|
||||
}
|
||||
}
|
||||
|
||||
RzBinElfReloc *reloc;
|
||||
rz_bin_elf_foreach_relocs(elf, reloc) {
|
||||
reloc->vaddr += base_vaddr;
|
||||
reloc->target_vaddr += base_vaddr;
|
||||
}
|
||||
|
||||
RzBinElfSymbol *symbol;
|
||||
rz_bin_elf_foreach_symbols(elf, symbol) {
|
||||
symbol->vaddr += base_vaddr;
|
||||
}
|
||||
}
|
||||
|
||||
static bool load_elf_obj_data(RZ_OUT RzBinMdtPart *part, RZ_OWN RzBinElfSegment *segment, RZ_OWN RzBinVirtualFile *vfile, RZ_OWN RzBinMap *map, bool big_endian) {
|
||||
rz_return_val_if_fail(part && segment && vfile && map, false);
|
||||
|
||||
ELFOBJ *elf = NULL;
|
||||
RzBinObjectLoadOptions obj_opts = {
|
||||
.force_elf_to_use_baddr = true,
|
||||
.baseaddr = 0,
|
||||
.loadaddr = 0,
|
||||
.elf_load_sections = segment->data.p_type == PT_LOAD,
|
||||
.patch_relocs = true,
|
||||
.big_endian = big_endian,
|
||||
};
|
||||
|
||||
elf = Elf32_rz_bin_elf_new_buf(vfile->buf, &obj_opts);
|
||||
if (!elf) {
|
||||
RZ_LOG_ERROR("Failed to load segment '%s' as ELF.\n", part->name);
|
||||
rz_buf_free(vfile->buf);
|
||||
rz_bin_map_free(map);
|
||||
free(vfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
part->obj.elf = elf;
|
||||
part->patches_vfile_name = rz_str_newf("patches.%s", part->name);
|
||||
part->relocs_vfile_name = rz_str_newf("relocs.%s", part->name);
|
||||
|
||||
normalize_vaddr_of_elf(part->obj.elf, part->map->vaddr);
|
||||
|
||||
// Maps - Add the normal ELF maps and the patch maps.
|
||||
part->sub_maps = patched_maps_elf_only(part->obj.elf, part->map->psize, part->vfile->buf, part->map->vaddr, part->patches_vfile_name, part->relocs_vfile_name);
|
||||
if (!part->sub_maps) {
|
||||
rz_warn_if_reached();
|
||||
return false;
|
||||
}
|
||||
void **it;
|
||||
rz_pvector_foreach (part->sub_maps, it) {
|
||||
RzBinMap *patched_elf_map = *it;
|
||||
char *part_relative_name = rz_str_newf("%s.%s", part->map->name, patched_elf_map->name);
|
||||
free(patched_elf_map->name);
|
||||
if (!patched_elf_map->vfile_name) {
|
||||
// This map has no vfile set. This means the IO layer will attempt to
|
||||
// read from the "main" file (the initially opened .mdt file). Of course unsuccessfully.
|
||||
// So we set the virtual file of the part here.
|
||||
patched_elf_map->vfile_name = strdup(part->map->name);
|
||||
}
|
||||
patched_elf_map->paddr += part->map->paddr;
|
||||
patched_elf_map->name = part_relative_name;
|
||||
}
|
||||
|
||||
// Symbols
|
||||
if (Elf_(rz_bin_elf_has_symbols)(part->obj.elf)) {
|
||||
RzPVector *elf_symbols = elf_symbols_obj(part->obj.elf);
|
||||
if (!elf_symbols) {
|
||||
rz_warn_if_reached();
|
||||
return false;
|
||||
}
|
||||
part->symbols = elf_symbols;
|
||||
}
|
||||
|
||||
// sections
|
||||
if (Elf_(rz_bin_elf_has_sections)(part->obj.elf)) {
|
||||
RzPVector *elf_sections = elf_sections_obj(part->obj.elf, part->map->psize);
|
||||
if (!elf_sections) {
|
||||
rz_warn_if_reached();
|
||||
return false;
|
||||
}
|
||||
while (!rz_pvector_empty(elf_sections)) {
|
||||
RzBinSection *section = rz_pvector_pop(elf_sections);
|
||||
// Fix the name for those. So they can be uniquely identified to which part they belong.
|
||||
prefix_name(§ion->name, part->name);
|
||||
rz_pvector_push(part->sections, section);
|
||||
}
|
||||
rz_pvector_free(elf_sections);
|
||||
}
|
||||
|
||||
// relocs
|
||||
if (Elf_(rz_bin_elf_has_relocs)(part->obj.elf) && part->obj.elf->buf_patched) {
|
||||
RzPVector *elf_relocs = elf_relocs_obj(part->obj.elf, part->map->vaddr, part->obj.elf->buf_patched);
|
||||
if (!elf_relocs) {
|
||||
rz_warn_if_reached();
|
||||
return false;
|
||||
}
|
||||
part->relocs = elf_relocs;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RzBinSection *elf_to_bin_segment(RzBinElfSegment *esegment, const char *name) {
|
||||
RzBinSection *bseg = RZ_NEW0(RzBinSection);
|
||||
rz_return_val_if_fail(bseg, NULL);
|
||||
|
||||
bseg->paddr = esegment->data.p_paddr;
|
||||
bseg->size = esegment->data.p_filesz;
|
||||
bseg->vsize = esegment->data.p_memsz;
|
||||
bseg->vaddr = esegment->data.p_vaddr;
|
||||
bseg->perm = esegment->data.p_flags;
|
||||
bseg->is_segment = true;
|
||||
bseg->is_data = !(esegment->data.p_flags & PF_X);
|
||||
bseg->align = esegment->data.p_align;
|
||||
bseg->flags = esegment->data.p_flags;
|
||||
bseg->name = rz_str_dup(name);
|
||||
return bseg;
|
||||
}
|
||||
|
||||
static RzBinMdtPart *segment_to_mdt_part(RzBinElfSegment *segment, size_t part_num, const char *suffix_less_path, bool big_endian) {
|
||||
RzBuffer *vfile_buffer = NULL;
|
||||
char *segment_file_path = NULL;
|
||||
RzBinMdtPart *part = NULL;
|
||||
RzBinMap *map = NULL;
|
||||
RzBinVirtualFile *vfile = NULL;
|
||||
|
||||
segment_file_path = rz_str_newf("%s.b%02" PFMTSZu, suffix_less_path, part_num);
|
||||
if (!segment_file_path) {
|
||||
rz_warn_if_reached();
|
||||
goto error;
|
||||
}
|
||||
const char *segment_name = rz_file_basename(segment_file_path);
|
||||
if (!segment_name) {
|
||||
segment_name = segment_file_path;
|
||||
}
|
||||
part = rz_bin_mdt_part_new(segment_name, segment->data.p_flags);
|
||||
|
||||
bool segment_file_exists = rz_file_exists(segment_file_path);
|
||||
bool zero_segment = segment->data.p_filesz == 0;
|
||||
if (zero_segment && segment_file_exists) {
|
||||
RZ_LOG_WARN("The segment size for '%s' is 0. But the file exists. Skip loading.\n", segment_file_path);
|
||||
goto error;
|
||||
} else if (!zero_segment && !segment_file_exists) {
|
||||
RZ_LOG_WARN("The segment size for '%s' is 0x%" PFMT32x ". But the file doesn't exist. Skip loading.\n", segment_file_path, segment->data.p_filesz);
|
||||
goto error;
|
||||
}
|
||||
|
||||
// Read <name>.bNN
|
||||
vfile_buffer = zero_segment ? rz_buf_new_empty(segment->data.p_memsz) : rz_buf_new_file(segment_file_path, O_RDONLY, 0);
|
||||
if (!vfile_buffer) {
|
||||
RZ_LOG_ERROR("Failed to read '%s'\n", segment_file_path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
vfile = RZ_NEW0(RzBinVirtualFile);
|
||||
if (!vfile) {
|
||||
goto error;
|
||||
}
|
||||
vfile->buf = vfile_buffer;
|
||||
vfile->buf_owned = true;
|
||||
vfile->name = strdup(part->name);
|
||||
|
||||
map = RZ_NEW0(RzBinMap);
|
||||
if (!map) {
|
||||
goto error;
|
||||
}
|
||||
map->paddr = 0;
|
||||
map->psize = segment->data.p_filesz;
|
||||
map->vsize = segment->data.p_memsz;
|
||||
map->vaddr = segment->data.p_vaddr;
|
||||
map->perm = segment->data.p_flags & (PF_X | PF_W | PF_R);
|
||||
map->vfile_name = strdup(part->name);
|
||||
map->name = strdup(part->name);
|
||||
|
||||
part->paddr = segment->data.p_paddr;
|
||||
part->pflags = segment->data.p_flags;
|
||||
part->map = map;
|
||||
part->vfile = vfile;
|
||||
part->sections = rz_pvector_new((RzPVectorFree)rz_bin_section_free);
|
||||
RzBinSection *bseg = elf_to_bin_segment(segment, part->name);
|
||||
if (!bseg) {
|
||||
goto error;
|
||||
}
|
||||
rz_pvector_push(part->sections, bseg);
|
||||
// Segments are also passed as Sections.
|
||||
if (is_elf32(vfile->buf)) {
|
||||
part->format = RZ_BIN_MDT_PART_ELF;
|
||||
if (!load_elf_obj_data(part, segment, vfile, map, big_endian)) {
|
||||
goto error;
|
||||
}
|
||||
} else if ((segment->data.p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_SIGNATURE) {
|
||||
part->format = RZ_BIN_MDT_PART_MBN;
|
||||
if (!load_mbn_obj_data(part, segment, vfile, map)) {
|
||||
// Not a critical error. Because it is irrelevant for the actual binary.
|
||||
RZ_LOG_WARN("Failed to load MBN signature segment. Header info won't be available.\n");
|
||||
}
|
||||
} else {
|
||||
part->format = RZ_BIN_MDT_PART_UNIDENTIFIED;
|
||||
if (!load_unidentified_obj_data(part, segment, vfile, map)) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
free(segment_file_path);
|
||||
return part;
|
||||
|
||||
error:
|
||||
rz_bin_mdt_part_free(part);
|
||||
free(segment_file_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RZ_IPI bool rz_bin_mdt_check_filename(const char *filename) {
|
||||
rz_return_val_if_fail(filename, NULL);
|
||||
if (!filename || strlen(filename) < strlen(".mdt")) {
|
||||
return false;
|
||||
}
|
||||
size_t len = strlen(filename);
|
||||
return filename[len - 4] == '.' && filename[len - 3] == 'm' && filename[len - 2] == 'd' && filename[len - 1] == 't';
|
||||
}
|
||||
|
||||
static char *get_peripheral_name(const char *filename) {
|
||||
if (!rz_bin_mdt_check_filename(filename)) {
|
||||
return NULL;
|
||||
}
|
||||
char *peripheral = rz_str_dup(filename);
|
||||
char *dot = strrchr(peripheral, '.');
|
||||
if (!dot) {
|
||||
free(peripheral);
|
||||
return NULL;
|
||||
}
|
||||
*dot = '\0';
|
||||
return peripheral;
|
||||
}
|
||||
|
||||
RZ_IPI bool rz_bin_mdt_load_buffer(RzBinFile *bf, RZ_OUT RzBinObject *obj, RzBuffer *buf, RZ_UNUSED Sdb *sdb) {
|
||||
rz_return_val_if_fail(obj && buf, false);
|
||||
if (!rz_bin_mdt_check_buffer(buf)) {
|
||||
RZ_LOG_ERROR("Unsupported binary.\n");
|
||||
return false;
|
||||
}
|
||||
RzBinMdtObj *mdt = rz_bin_mdt_obj_new();
|
||||
if (!mdt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mdt->name = get_peripheral_name(bf->file);
|
||||
if (!mdt->name) {
|
||||
RZ_LOG_ERROR("Filename \"%s\" doesn't indicate it is an .mdt peripheral image.\n", bf->file);
|
||||
goto error;
|
||||
}
|
||||
|
||||
RzBinObjectLoadOptions obj_opts = {
|
||||
.baseaddr = UT64_MAX,
|
||||
.loadaddr = 0,
|
||||
.elf_load_sections = false,
|
||||
};
|
||||
mdt->header = Elf32_rz_bin_elf_new_buf(buf, &obj_opts);
|
||||
if (!mdt->header) {
|
||||
RZ_LOG_ERROR("Failed to parse .mdt ELF header.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
RzBinElfSegment *segment;
|
||||
rz_vector_enumerate (mdt->header->segments, segment, i) {
|
||||
RzBinMdtPart *part = segment_to_mdt_part(segment, i, mdt->name, mdt->header->big_endian);
|
||||
if (!part) {
|
||||
continue;
|
||||
}
|
||||
rz_pvector_push(mdt->parts, part);
|
||||
}
|
||||
|
||||
obj->bin_obj = mdt;
|
||||
return true;
|
||||
|
||||
error:
|
||||
rz_bin_mdt_obj_free(mdt);
|
||||
return false;
|
||||
}
|
||||
|
||||
RZ_IPI void rz_bin_mdt_destroy(RzBinFile *bf) {
|
||||
rz_return_if_fail(bf && bf->o && bf->o->bin_obj);
|
||||
rz_bin_mdt_obj_free(bf->o->bin_obj);
|
||||
}
|
||||
|
||||
RZ_IPI RZ_OWN RzPVector /*<RzBinVirtualFile *>*/ *rz_bin_mdt_virtual_files(RzBinFile *bf) {
|
||||
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
|
||||
const RzBinMdtObj *mdt = bf->o->bin_obj;
|
||||
RzPVector *vfiles = rz_pvector_new((RzPVectorFree)rz_bin_virtual_file_free);
|
||||
void **it;
|
||||
rz_pvector_foreach (mdt->parts, it) {
|
||||
RzBinMdtPart *part = *it;
|
||||
RzBinVirtualFile *clone = rz_bin_virtual_file_clone(part->vfile);
|
||||
if (!clone) {
|
||||
continue;
|
||||
}
|
||||
rz_pvector_push(vfiles, clone);
|
||||
if (!part->relocs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (part->patches_vfile_name) {
|
||||
RzBinVirtualFile *patches = RZ_NEW0(RzBinVirtualFile);
|
||||
patches->buf = part->obj.elf->buf_patched;
|
||||
patches->buf_owned = false;
|
||||
patches->name = rz_str_dup(part->patches_vfile_name);
|
||||
rz_pvector_push(vfiles, patches);
|
||||
}
|
||||
if (part->relocs_vfile_name) {
|
||||
ut64 reloc_size = elf_reloc_targets_vfile_size(part->obj.elf);
|
||||
if (!reloc_size) {
|
||||
continue;
|
||||
}
|
||||
RzBuffer *buf = rz_buf_new_empty(reloc_size);
|
||||
RzBinVirtualFile *relocs = RZ_NEW0(RzBinVirtualFile);
|
||||
if (!relocs || !buf) {
|
||||
rz_buf_free(buf);
|
||||
free(relocs);
|
||||
continue;
|
||||
}
|
||||
relocs->buf = buf;
|
||||
relocs->buf_owned = true;
|
||||
relocs->name = rz_str_dup(part->relocs_vfile_name);
|
||||
rz_pvector_push(vfiles, relocs);
|
||||
}
|
||||
}
|
||||
return vfiles;
|
||||
}
|
||||
|
||||
RZ_IPI RZ_OWN RzPVector /*<RzBinMap *>*/ *rz_bin_mdt_get_maps(RzBinFile *bf) {
|
||||
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
|
||||
const RzBinMdtObj *mdt = bf->o->bin_obj;
|
||||
RzPVector *maps = rz_pvector_new((RzPVectorFree)rz_bin_map_free);
|
||||
if (!maps) {
|
||||
return NULL;
|
||||
}
|
||||
void **it;
|
||||
rz_pvector_foreach (mdt->parts, it) {
|
||||
RzBinMdtPart *part = *it;
|
||||
if (!part->sub_maps || part->is_layout) {
|
||||
// The first ELF file is always the overall firmware layout.
|
||||
RzBinMap *clone = rz_bin_map_clone(part->map);
|
||||
if (!clone) {
|
||||
continue;
|
||||
}
|
||||
rz_pvector_push(maps, clone);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the patched ELF maps
|
||||
void **it;
|
||||
rz_pvector_foreach (part->sub_maps, it) {
|
||||
RzBinMap *clone = rz_bin_map_clone(*it);
|
||||
if (!clone) {
|
||||
continue;
|
||||
}
|
||||
rz_pvector_push(maps, clone);
|
||||
}
|
||||
}
|
||||
return maps;
|
||||
}
|
||||
|
||||
RZ_IPI RZ_OWN RzPVector /*<RzBinAddr *>*/ *rz_bin_mdt_get_entry_points(RzBinFile *bf) {
|
||||
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
|
||||
const RzBinMdtObj *mdt = bf->o->bin_obj;
|
||||
RzPVector *entries = rz_pvector_new((RzPVectorFree)free);
|
||||
if (!entries) {
|
||||
return NULL;
|
||||
}
|
||||
RzBinAddr *entry = elf_addr_new_from_paddr(mdt->header, mdt->header->ehdr.e_entry);
|
||||
if (!entry) {
|
||||
rz_pvector_free(entries);
|
||||
return NULL;
|
||||
}
|
||||
// For the firmware initialization the entry is at a physical address of course.
|
||||
// We search for the fitting segment and calculate the virtual address.
|
||||
// Because Rizin can't handle the MMU physical address space.
|
||||
RzBinElfSegment *seg;
|
||||
rz_bin_elf_foreach_segments(mdt->header, seg) {
|
||||
RzIntervalBoundedUt64 seg_phy_range;
|
||||
seg_phy_range.a = seg->data.p_paddr;
|
||||
seg_phy_range.b = seg->data.p_paddr + seg->data.p_memsz;
|
||||
seg_phy_range.bound = RZ_INTERVAL_BOUND_RIGHT_OPEN;
|
||||
|
||||
if (rz_itv_bound_contains_ut64(&seg_phy_range, entry->paddr) == RZ_INTERVAL_IN) {
|
||||
entry->paddr = (entry->paddr - seg_phy_range.a);
|
||||
entry->vaddr = seg->data.p_vaddr + entry->paddr;
|
||||
entry->type = RZ_BIN_ENTRY_TYPE_INIT;
|
||||
entry->bits = mdt->header->bits;
|
||||
break;
|
||||
}
|
||||
}
|
||||
rz_pvector_push(entries, entry);
|
||||
return entries;
|
||||
}
|
||||
|
||||
RZ_IPI RZ_OWN RzPVector /*<RzBinSymbol *>*/ *rz_bin_mdt_symbols(RzBinFile *bf) {
|
||||
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
|
||||
const RzBinMdtObj *mdt = bf->o->bin_obj;
|
||||
RzPVector *symbols = rz_pvector_new((RzPVectorFree)rz_bin_symbol_free);
|
||||
if (!symbols) {
|
||||
return NULL;
|
||||
}
|
||||
void **it;
|
||||
rz_pvector_foreach (mdt->parts, it) {
|
||||
RzBinMdtPart *part = *it;
|
||||
while (part->symbols && !rz_pvector_empty(part->symbols)) {
|
||||
rz_pvector_push(symbols, rz_pvector_pop(part->symbols));
|
||||
}
|
||||
}
|
||||
return symbols;
|
||||
}
|
||||
|
||||
RZ_IPI RZ_OWN RzPVector /*<RzBinSection *>*/ *rz_bin_mdt_sections(RzBinFile *bf) {
|
||||
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
|
||||
const RzBinMdtObj *mdt = bf->o->bin_obj;
|
||||
RzPVector *sections = rz_pvector_new((RzPVectorFree)rz_bin_section_free);
|
||||
if (!sections) {
|
||||
return NULL;
|
||||
}
|
||||
void **it;
|
||||
rz_pvector_foreach (mdt->parts, it) {
|
||||
RzBinMdtPart *part = *it;
|
||||
while (part->sections && !rz_pvector_empty(part->sections)) {
|
||||
rz_pvector_push(sections, rz_pvector_pop(part->sections));
|
||||
}
|
||||
}
|
||||
return sections;
|
||||
}
|
||||
|
||||
RZ_IPI RZ_OWN RzPVector /*<RzBinReloc *>*/ *rz_bin_mdt_relocs(RzBinFile *bf) {
|
||||
rz_return_val_if_fail(bf && bf->o && bf->o->bin_obj, NULL);
|
||||
const RzBinMdtObj *mdt = bf->o->bin_obj;
|
||||
RzPVector *relocs = rz_pvector_new((RzPVectorFree)rz_bin_reloc_free);
|
||||
if (!relocs) {
|
||||
return NULL;
|
||||
}
|
||||
void **it;
|
||||
rz_pvector_foreach (mdt->parts, it) {
|
||||
RzBinMdtPart *part = *it;
|
||||
while (part->relocs && !rz_pvector_empty(part->relocs)) {
|
||||
rz_pvector_push(relocs, rz_pvector_pop(part->relocs));
|
||||
}
|
||||
}
|
||||
return relocs;
|
||||
}
|
||||
|
||||
RZ_IPI void rz_bin_mdt_print_header(RzBinFile *bf) {
|
||||
rz_return_if_fail(bf && bf->o && bf->o->bin_obj && bf->rbin && bf->rbin->cb_printf);
|
||||
const RzBinMdtObj *mdt = bf->o->bin_obj;
|
||||
char bits[65] = { 0 };
|
||||
size_t i;
|
||||
void **it;
|
||||
rz_pvector_enumerate (mdt->parts, it, i) {
|
||||
RzBinMdtPart *part = *it;
|
||||
rz_str_bits64(bits, qcom_p_flags(part->pflags));
|
||||
bf->rbin->cb_printf("==== MDT Segment %" PFMTSZu " ====\n", i);
|
||||
bf->rbin->cb_printf(" priv_p_flags: 0b%s:", bits);
|
||||
if (part->is_layout) {
|
||||
bf->rbin->cb_printf(" layout");
|
||||
}
|
||||
if (part->relocatable) {
|
||||
bf->rbin->cb_printf(" reloc");
|
||||
}
|
||||
switch (part->format) {
|
||||
default:
|
||||
case RZ_BIN_MDT_PART_UNIDENTIFIED:
|
||||
bf->rbin->cb_printf(" | Unidentified\n");
|
||||
break;
|
||||
case RZ_BIN_MDT_PART_ELF:
|
||||
bf->rbin->cb_printf(" | ELF\n");
|
||||
if (part->obj.elf) {
|
||||
bf->rbin->cb_printf(" -- ELF HEADER BEGIN -- \n");
|
||||
elf_headers_obj((ELFOBJ *)part->obj.elf, bf->rbin->cb_printf);
|
||||
bf->rbin->cb_printf(" --- ELF HEADER END --- \n\n");
|
||||
} else {
|
||||
bf->rbin->cb_printf(" ------- FAILED ------- \n");
|
||||
}
|
||||
break;
|
||||
case RZ_BIN_MDT_PART_MBN:
|
||||
bf->rbin->cb_printf(" | MBN signature segment\n");
|
||||
if (part->obj.mbn) {
|
||||
bf->rbin->cb_printf(" -- MBN AUTH HEADER BEGIN -- \n");
|
||||
mbn_header_obj(part->obj.mbn, bf->rbin->cb_printf);
|
||||
bf->rbin->cb_printf(" --- MBN AUTH HEADER END --- \n\n");
|
||||
} else {
|
||||
bf->rbin->cb_printf(" ------- FAILED ------- \n");
|
||||
}
|
||||
break;
|
||||
case RZ_BIN_MDT_PART_COMPRESSED_Q6ZIP:
|
||||
bf->rbin->cb_printf(" | Q6ZIP compressed\n");
|
||||
break;
|
||||
case RZ_BIN_MDT_PART_COMPRESSED_CLADE2:
|
||||
bf->rbin->cb_printf(" | CLADE2 compressed\n");
|
||||
break;
|
||||
case RZ_BIN_MDT_PART_COMPRESSED_ZLIB:
|
||||
bf->rbin->cb_printf(" | ZLIB compressed\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
101
librz/bin/format/mdt/mdt.h
Normal file
101
librz/bin/format/mdt/mdt.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// SPDX-FileCopyrightText: 2025 Rot127 <unisono@quyllur.org>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
/**
|
||||
* \file Loader for the Qualcomm peripheral firmware images.
|
||||
*
|
||||
* Reference: https://github.com/torvalds/linux/blob/master/drivers/soc/qcom/mdt_loader.c
|
||||
*/
|
||||
|
||||
#ifndef RZ_MDT_H
|
||||
#define RZ_MDT_H
|
||||
|
||||
#include <rz_bin.h>
|
||||
#include "../format/elf/elf.h"
|
||||
#include "../format/mbn/mbn.h"
|
||||
|
||||
#define qcom_p_flags(p_flags) (p_flags >> 24)
|
||||
|
||||
/**
|
||||
* \brief Mask for the segment type.
|
||||
*/
|
||||
#define QCOM_MDT_TYPE_MASK (7 << 24)
|
||||
/**
|
||||
* \brief Bits set for the first firmware part.
|
||||
*/
|
||||
#define QCOM_MDT_TYPE_LAYOUT (7 << 24)
|
||||
/**
|
||||
* \brief Type of the signature segment.
|
||||
*/
|
||||
#define QCOM_MDT_TYPE_SIGNATURE (2 << 24)
|
||||
/**
|
||||
* \brief Relocatable segment.
|
||||
*/
|
||||
#define QCOM_MDT_RELOCATABLE (1 << 27)
|
||||
|
||||
/**
|
||||
* \brief The segment type/p_type as it is in the ELF.
|
||||
*/
|
||||
typedef ut32 RzBinMdtPFlags;
|
||||
|
||||
typedef enum rz_bin_mdt_seg_type {
|
||||
RZ_BIN_MDT_PART_UNIDENTIFIED = 0,
|
||||
RZ_BIN_MDT_PART_ELF, ///< An ELF file.
|
||||
RZ_BIN_MDT_PART_MBN, ///< The secure boot authentication signature segment.
|
||||
RZ_BIN_MDT_PART_COMPRESSED_Q6ZIP, ///< Q6ZIP compressed segment (if identified).
|
||||
RZ_BIN_MDT_PART_COMPRESSED_CLADE2, ///< CLADE2 compressed segment (if identified).
|
||||
RZ_BIN_MDT_PART_COMPRESSED_ZLIB, ///< Zlib compressed segment (if identified).
|
||||
} RzBinMdtSegBinFormat;
|
||||
|
||||
/**
|
||||
* \brief An MDT firmware part and some descriptions.
|
||||
*/
|
||||
typedef struct {
|
||||
char *name; ///< The name of the part. Should be equal to the base name of the file.
|
||||
bool relocatable; ///< True if the Qualcomm relocatable flag is set for the segment.
|
||||
bool is_layout; ///< True if the ELF segment is the firmware layout.
|
||||
RzBinMdtSegBinFormat format; ///< The segment type.
|
||||
RzBinMdtPFlags pflags; ///< The segment p_flags.
|
||||
RzBinVirtualFile *vfile; ///< The virtual file for the `.bNN` file.
|
||||
union {
|
||||
ELFOBJ *elf; ///< Set if this part is an ELF.
|
||||
SblHeader *mbn; ///< Set if this part is an MBN auth segment.
|
||||
} obj;
|
||||
RzBinAddr *entry; ///< The entry point, if any.
|
||||
RzBinMap *map; ///< The mapping of the part in memory.
|
||||
/**
|
||||
* \brief The physical address as in the layout. This is not the same as map->paddr!
|
||||
* Because map is used to read from the files. So it has be zero (to not mess up the reading offsets).
|
||||
*/
|
||||
ut64 paddr;
|
||||
char *patches_vfile_name; ///< Name of the vfile of patches to the binary. If NULL, no patches are supported.
|
||||
char *relocs_vfile_name; ///< Name of the vfile of relocs to the binary. If NULL, no relocs are supported.
|
||||
RzPVector /*<RzBinSymbol *>*/ *symbols; ///< Symbols in this part.
|
||||
RzPVector /*<RzBinReloc *>*/ *relocs; ///< Relocs in this part.
|
||||
RzPVector /*<RzBinSection *>*/ *sections; ///< Sections in this part.
|
||||
RzPVector /*<RzBinMap *>*/ *sub_maps; ///< Maps of the obj, if any.
|
||||
} RzBinMdtPart;
|
||||
|
||||
typedef struct {
|
||||
char *name; ///< The name of the peripheral firmware. E.g. modem, adsp, cdsp or npu.
|
||||
ELFOBJ *header; ///< The ELF header of the whole firmware. From `<peripheral>.mdt`.
|
||||
RzPVector /*<RzBinMdtPart *>*/ *parts; ///< All parts from the `<peripheral>.bNN` files.
|
||||
} RzBinMdtObj;
|
||||
|
||||
RZ_IPI RzBinMdtPart *rz_bin_mdt_part_new(const char *name, size_t p_flags);
|
||||
RZ_IPI void rz_bin_mdt_part_free(RZ_OWN RZ_NULLABLE RzBinMdtPart *part);
|
||||
RZ_IPI RzBinMdtObj *rz_bin_mdt_obj_new();
|
||||
RZ_IPI void rz_bin_mdt_obj_free(RzBinMdtObj *obj);
|
||||
RZ_IPI bool rz_bin_mdt_check_filename(const char *filename);
|
||||
RZ_IPI bool rz_bin_mdt_load_buffer(RZ_UNUSED RzBinFile *bf, RZ_OUT RzBinObject *obj, RzBuffer *buf, RZ_UNUSED Sdb *sdb);
|
||||
RZ_IPI bool rz_bin_mdt_check_buffer(RzBuffer *b);
|
||||
RZ_IPI void rz_bin_mdt_destroy(RzBinFile *bf);
|
||||
RZ_IPI RZ_OWN RzPVector /*<RzBinMap *>*/ *rz_bin_mdt_get_maps(RzBinFile *bf);
|
||||
RZ_IPI RZ_OWN RzPVector /*<RzBinAddr *>*/ *rz_bin_mdt_get_entry_points(RzBinFile *bf);
|
||||
RZ_IPI RZ_OWN RzPVector /*<RzBinVirtualFile *>*/ *rz_bin_mdt_virtual_files(RzBinFile *bf);
|
||||
RZ_IPI RZ_OWN RzPVector /*<RzBinSymbol *>*/ *rz_bin_mdt_symbols(RzBinFile *bf);
|
||||
RZ_IPI void rz_bin_mdt_print_header(RzBinFile *bf);
|
||||
RZ_IPI RZ_OWN RzPVector /*<RzBinSection *>*/ *rz_bin_mdt_sections(RzBinFile *bf);
|
||||
RZ_IPI RZ_OWN RzPVector /*<RzBinReloc *>*/ *rz_bin_mdt_relocs(RzBinFile *bf);
|
||||
|
||||
#endif // RZ_MDT_H
|
||||
|
|
@ -21,6 +21,7 @@ bin_plugins_list = [
|
|||
'mach064',
|
||||
'mbn',
|
||||
'mdmp',
|
||||
'mdt',
|
||||
'menuet',
|
||||
'mz',
|
||||
'ne',
|
||||
|
|
@ -117,6 +118,7 @@ rz_bin_sources = [
|
|||
'p/bin_mach064.c',
|
||||
'p/bin_mbn.c',
|
||||
'p/bin_mdmp.c',
|
||||
'p/bin_mdt.c',
|
||||
'p/bin_menuet.c',
|
||||
'p/bin_mz.c',
|
||||
'p/bin_ne.c',
|
||||
|
|
@ -220,6 +222,7 @@ rz_bin_sources = [
|
|||
'format/mdmp/mdmp.c',
|
||||
'format/mdmp/mdmp_pe.c',
|
||||
'format/mdmp/mdmp_pe64.c',
|
||||
'format/mdt/mdt.c',
|
||||
'format/le/le.c',
|
||||
'format/luac/luac_common.c',
|
||||
'format/luac/luac_bin.c',
|
||||
|
|
|
|||
60
librz/bin/p/bin_mdt.c
Normal file
60
librz/bin/p/bin_mdt.c
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// SPDX-FileCopyrightText: 2025 Rot127 <unisono@quyllur.org>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_bin.h>
|
||||
#include "../format/mdt/mdt.h"
|
||||
|
||||
static RzBinInfo *mdt_info(RzBinFile *bf) {
|
||||
RzBinInfo *ret = RZ_NEW0(RzBinInfo);
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
RzBinMdtObj *mdt = bf->o->bin_obj;
|
||||
ret->lang = "";
|
||||
ret->file = rz_str_dup(bf->file);
|
||||
ret->type = rz_str_dup("mdt");
|
||||
ret->has_pi = 0;
|
||||
ret->has_canary = 0;
|
||||
ret->has_retguard = -1;
|
||||
ret->big_endian = Elf_(rz_bin_elf_is_big_endian)(mdt->header);
|
||||
ret->has_va = Elf_(rz_bin_elf_has_va)(mdt->header);
|
||||
ret->has_nx = Elf_(rz_bin_elf_has_nx)(mdt->header);
|
||||
ret->intrp = Elf_(rz_bin_elf_get_intrp)(mdt->header);
|
||||
ret->compiler = Elf_(rz_bin_elf_get_compiler)(mdt->header);
|
||||
ret->dbg_info = 0;
|
||||
ret->bits = 32;
|
||||
ret->arch = Elf32_rz_bin_elf_get_arch(mdt->header);
|
||||
ret->cpu = Elf32_rz_bin_elf_get_cpu(mdt->header);
|
||||
ret->machine = Elf32_rz_bin_elf_get_machine_name(mdt->header);
|
||||
return ret;
|
||||
}
|
||||
|
||||
RzBinPlugin rz_bin_plugin_mdt = {
|
||||
.name = "mdt",
|
||||
.desc = "Qualcomm Peripheral Image Loader (32bit only)",
|
||||
.author = "Rot127",
|
||||
.license = "LGPL3",
|
||||
.check_filename = &rz_bin_mdt_check_filename,
|
||||
.load_buffer = &rz_bin_mdt_load_buffer,
|
||||
.info = &mdt_info,
|
||||
.header = &rz_bin_mdt_print_header,
|
||||
.maps = &rz_bin_mdt_get_maps,
|
||||
.entries = &rz_bin_mdt_get_entry_points,
|
||||
.check_buffer = &rz_bin_mdt_check_buffer,
|
||||
.virtual_files = &rz_bin_mdt_virtual_files,
|
||||
.destroy = &rz_bin_mdt_destroy,
|
||||
.hashes = NULL,
|
||||
.sections = &rz_bin_mdt_sections,
|
||||
.symbols = &rz_bin_mdt_symbols,
|
||||
.imports = NULL,
|
||||
.libs = NULL,
|
||||
.relocs = rz_bin_mdt_relocs,
|
||||
};
|
||||
|
||||
#ifndef RZ_PLUGIN_INCORE
|
||||
RZ_API RzLibStruct rizin_plugin = {
|
||||
.type = RZ_LIB_TYPE_BIN,
|
||||
.data = &rz_bin_plugin_mdt,
|
||||
.version = RZ_VERSION
|
||||
};
|
||||
#endif
|
||||
|
|
@ -868,7 +868,9 @@ RZ_API RzBinClassField *rz_bin_class_field_new(ut64 vaddr, ut64 paddr, const cha
|
|||
RZ_API void rz_bin_class_field_free(RZ_NULLABLE RzBinClassField *field);
|
||||
RZ_API void rz_bin_class_free(RZ_NULLABLE RzBinClass *k);
|
||||
|
||||
RZ_API RZ_OWN RzBinVirtualFile *rz_bin_virtual_file_clone(RZ_BORROW RZ_NONNULL RzBinVirtualFile *vfile);
|
||||
RZ_API void rz_bin_virtual_file_free(RZ_NULLABLE RzBinVirtualFile *vfile);
|
||||
RZ_API RZ_OWN RzBinMap *rz_bin_map_clone(RZ_NONNULL RzBinMap *map);
|
||||
RZ_API void rz_bin_map_free(RZ_NULLABLE RzBinMap *map);
|
||||
RZ_API bool rz_bin_map_is_data(RZ_NONNULL const RzBinMap *map);
|
||||
RZ_API RZ_OWN RzPVector /*<RzBinMap *>*/ *rz_bin_maps_of_file_sections(RZ_NONNULL RzBinFile *binfile);
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
382
test/db/formats/mdt
Normal file
382
test/db/formats/mdt
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
NAME=iH
|
||||
FILE=bins/mdt/bin-mdt/load-test.mdt
|
||||
CMDS=<<EOF
|
||||
iH
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
==== MDT Segment 0 ====
|
||||
priv_p_flags: 0b00000111: layout | ELF
|
||||
-- ELF HEADER BEGIN --
|
||||
0x00000000 MAGIC 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
|
||||
0x00000010 Type 0x0002
|
||||
0x00000012 Machine 0x00a4
|
||||
0x00000014 Version 0x00000001
|
||||
0x00000018 Entrypoint 0x87400000
|
||||
0x0000001c PhOff 0x00000034
|
||||
0x00000020 ShOff 0x00000000
|
||||
0x00000024 Flags 0x0066
|
||||
0x00000028 EhSize 52
|
||||
0x0000002a PhentSize 32
|
||||
0x0000002c PhNum 5
|
||||
0x0000002e ShentSize 40
|
||||
0x00000030 ShNum 0
|
||||
0x00000032 ShStrndx 0
|
||||
--- ELF HEADER END ---
|
||||
|
||||
==== MDT Segment 1 ====
|
||||
priv_p_flags: 0b00000010: | MBN signature segment
|
||||
-- MBN AUTH HEADER BEGIN --
|
||||
0x00 image_id: kMbnImageNone (0x0)
|
||||
0x04 version: 0x0
|
||||
0x08 paddr: 0x0
|
||||
0x0c vaddr: 0x0
|
||||
0x10 psize: 0x0
|
||||
0x14 code_pa: 0x0
|
||||
0x18 sign_va: 0x0
|
||||
0x1c sign_sz: 0x0
|
||||
0x20 cert_va: 0x0
|
||||
0x24 cert_sz: 0x0
|
||||
--- MBN AUTH HEADER END ---
|
||||
|
||||
==== MDT Segment 2 ====
|
||||
priv_p_flags: 0b00001000: reloc | Unidentified
|
||||
==== MDT Segment 3 ====
|
||||
priv_p_flags: 0b00001000: reloc | ELF
|
||||
-- ELF HEADER BEGIN --
|
||||
0x00000000 MAGIC 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
|
||||
0x00000010 Type 0x0002
|
||||
0x00000012 Machine 0x00a4
|
||||
0x00000014 Version 0x00000001
|
||||
0x00000018 Entrypoint 0x00000000
|
||||
0x0000001c PhOff 0x00000034
|
||||
0x00000020 ShOff 0x00011ee0
|
||||
0x00000024 Flags 0x0060
|
||||
0x00000028 EhSize 52
|
||||
0x0000002a PhentSize 32
|
||||
0x0000002c PhNum 3
|
||||
0x0000002e ShentSize 40
|
||||
0x00000030 ShNum 16
|
||||
0x00000032 ShStrndx 13
|
||||
--- ELF HEADER END ---
|
||||
|
||||
==== MDT Segment 4 ====
|
||||
priv_p_flags: 0b00001000: reloc | ELF
|
||||
-- ELF HEADER BEGIN --
|
||||
0x00000000 MAGIC 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
|
||||
0x00000010 Type 0x0001
|
||||
0x00000012 Machine 0x00a4
|
||||
0x00000014 Version 0x00000001
|
||||
0x00000018 Entrypoint 0x00000000
|
||||
0x0000001c PhOff 0x00000000
|
||||
0x00000020 ShOff 0x000004ec
|
||||
0x00000024 Flags 0x0060
|
||||
0x00000028 EhSize 52
|
||||
0x0000002a PhentSize 0
|
||||
0x0000002c PhNum 0
|
||||
0x0000002e ShentSize 40
|
||||
0x00000030 ShNum 5
|
||||
0x00000032 ShStrndx 1
|
||||
--- ELF HEADER END ---
|
||||
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=oml
|
||||
FILE=bins/mdt/bin-mdt/load-test.mdt
|
||||
CMDS=<<EOF
|
||||
oml
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
1 fd: 4 +0x00000000 0x89201c18 - 0x89201fff --- mmap.load-test.b01
|
||||
2 fd: 5 +0x00000000 0x89200000 - 0x89201c17 --- vmap.load-test.b01
|
||||
3 fd: 6 +0x00000000 0xfe030000 - 0xfe0aefff r-x mmap.load-test.b02
|
||||
4 fd: 7 +0x00000000 0xfe000000 * 0xfe02ffff r-x vmap.load-test.b02
|
||||
5 fd: 8 +0x00001000 0x00550000 - 0x00553ebf r-x vmap.load-test.b03.LOAD0
|
||||
6 fd: 8 +0x00005000 0x00554000 - 0x0055b763 r-x vmap.load-test.b03.LOAD1
|
||||
7 fd: 9 +0x00000000 0x0055d0a8 - 0x0055df03 rw- mmap.load-test.b03.LOAD2
|
||||
8 fd: 8 +0x0000d000 0x0055c000 - 0x0055d0a7 r-- vmap.load-test.b03.LOAD2
|
||||
9 fd: 10 +0x00000000 0x000004f4 - 0x00000567 r-- vmap.load-test.b04.reloc-targets
|
||||
10 fd: 11 +0x00000388 0xc00c9388 - 0xc00c94e9 --- vmap.load-test.b04..strtab
|
||||
11 fd: 12 +0x00000034 0xc00c9034 - 0xc00c90bb r-x vmap.load-test.b04..text
|
||||
12 fd: 11 +0x0000022c 0xc00c922c - 0xc00c9387 --x vmap.load-test.b04..rela.text
|
||||
13 fd: 11 +0x000000bc 0xc00c90bc - 0xc00c922b --- vmap.load-test.b04..symtab
|
||||
14 fd: 11 +0x00000000 0xc00c9000 - 0xc00c9033 r-- vmap.load-test.b04.ehdr
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=iSS - segments
|
||||
FILE=bins/mdt/bin-mdt/load-test.mdt
|
||||
CMDS=<<EOF
|
||||
echo "======== Sections ========"
|
||||
iS
|
||||
echo "======== Segments ========"
|
||||
iSS
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
======== Sections ========
|
||||
paddr size vaddr vsize align perm name type flags
|
||||
----------------------------------------------------------------------------------
|
||||
0x00000000 0x0 ---------- 0x0 0x0 ---- load-test.b03.0x0
|
||||
0x00001000 0x3ec0 0x00550000 0x3ec0 0x0 -rwx load-test.b03.start
|
||||
0x00005000 0x54 0x00554000 0x54 0x0 -r-x load-test.b03.init
|
||||
0x00006000 0x6088 0x00555000 0x6088 0x0 -r-x load-test.b03.text
|
||||
0x0000c0a0 0x30 0x0055b0a0 0x30 0x0 -r-x load-test.b03.fini
|
||||
0x0000c0d0 0x688 0x0055b0d0 0x688 0x0 -r-- load-test.b03.rodata
|
||||
0x0000c760 0x4 0x0055b760 0x4 0x0 -r-- load-test.b03.eh_frame
|
||||
0x0000d000 0x708 0x0055c000 0x708 0x0 -rw- load-test.b03.data
|
||||
0x0000d708 0x10 0x0055c708 0x10 0x0 -rw- load-test.b03.ctors
|
||||
0x0000d718 0x14 0x0055c718 0x14 0x0 -rw- load-test.b03.dtors
|
||||
0x0000e000 0xa8 0x0055d000 0xa8 0x0 -rw- load-test.b03.sdata
|
||||
0x0000e0a8 0x0 0x0055d0a8 0xe5c 0x0 -rw- load-test.b03.bss
|
||||
0x0000e0a8 0xb5 ---------- 0xb5 0x0 ---- load-test.b03.comment
|
||||
0x0000e15d 0x6f ---------- 0x6f 0x0 ---- load-test.b03.shstrtab
|
||||
0x0000e1cc 0x2310 ---------- 0x2310 0x0 ---- load-test.b03.symtab
|
||||
0x000104dc 0x19f1 ---------- 0x19f1 0x0 ---- load-test.b03.strtab
|
||||
0x00000000 0x0 0xc00c9000 0x0 0x0 ---- load-test.b04.0x0
|
||||
0x00000388 0x162 0xc00c9388 0x162 0x0 ---- load-test.b04.strtab
|
||||
0x00000034 0x88 0xc00c9034 0x88 0x0 -r-x load-test.b04.text
|
||||
0x0000022c 0x15c 0xc00c922c 0x15c 0x0 ---- load-test.b04.rela.text
|
||||
0x000000bc 0x170 0xc00c90bc 0x170 0x0 ---- load-test.b04.symtab
|
||||
======== Segments ========
|
||||
paddr size vaddr vsize align perm name
|
||||
------------------------------------------------------------------------
|
||||
0x00000000 0x1d4 0x00000000 0x0 0x0 ---- load-test.b00
|
||||
0x89200000 0x1c18 0x89200000 0x2000 0x1000 ---- load-test.b01
|
||||
0x87400000 0x30000 0xfe000000 0xaf000 0x100000 -r-x load-test.b02
|
||||
0x00001000 0x3ec0 0x00550000 0x3ec0 0x1000 -rwx load-test.b03.LOAD0
|
||||
0x00005000 0x7764 0x00554000 0x7764 0x1000 -r-x load-test.b03.LOAD1
|
||||
0x0000d000 0x10a8 0x0055c000 0x1f04 0x1000 -rw- load-test.b03.LOAD2
|
||||
0x00000000 0x34 0x00550000 0x34 0x0 -rw- load-test.b03.ehdr
|
||||
0x874af000 0x12160 0x00550000 0x1a000 0x1000 -r-x load-test.b03
|
||||
0x00000000 0x34 0xc00c9000 0x34 0x0 -rw- load-test.b04.ehdr
|
||||
0x874c9000 0x5b4 0xc00c9000 0x1000 0x1000 -r-x load-test.b04
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=ie - entries
|
||||
FILE=bins/mdt/bin-mdt/load-test.mdt
|
||||
CMDS=<<EOF
|
||||
ie
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
vaddr paddr hvaddr haddr type
|
||||
-------------------------------------------------
|
||||
0xfe000000 0x00000000 ---------- ---------- init
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=ir - relocs print
|
||||
FILE=bins/mdt/bin-mdt/load-test.mdt
|
||||
CMDS=<<EOF
|
||||
|
||||
# This should give the same (relative) result as the reloc tests in db/analysis/hexagon.
|
||||
# The load-bin.b04 is the same binary as used for the reloc tests there.
|
||||
|
||||
echo "==== Print relocs ===="
|
||||
ir
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
==== Print relocs ====
|
||||
vaddr paddr target type name
|
||||
---------------------------------------------------------------------------
|
||||
0xc00c9048 0x00000048 0xc00c9034 R_HEX_B15_PCREL test_sym - 0xc00c9048
|
||||
0xc00c904c 0x0000004c 0xc00c9034 R_HEX_B7_PCREL test_sym - 0xc00c904c
|
||||
0xc00c9050 0x00000050 0xc00c9034 R_HEX_LO16 test_sym
|
||||
0xc00c9054 0x00000054 0xc00c9034 R_HEX_HI16 test_sym
|
||||
0xc00c9058 0x00000058 0xc00c9034 R_HEX_B13_PCREL test_sym - 0xc00c9058
|
||||
0xc00c905c 0x0000005c 0xc00c9034 R_HEX_B9_PCREL test_sym - 0xc00c905c
|
||||
0xc00c9060 0x00000060 0xc00c9034 R_HEX_B32_PCREL_X test_sym - 0xc00c9060
|
||||
0xc00c9064 0x00000064 0xc00c9034 R_HEX_B22_PCREL_X test_sym - 0xc00c9060
|
||||
0xc00c9068 0x00000068 0xc00c9034 R_HEX_32_6_X test_sym
|
||||
0xc00c906c 0x0000006c 0xc00c9034 R_HEX_16_X test_sym
|
||||
0xc00c9070 0x00000070 0xc00c9034 R_HEX_B32_PCREL_X test_sym - 0xc00c9070
|
||||
0xc00c9074 0x00000074 0xc00c9034 R_HEX_B22_PCREL_X test_sym - 0xc00c9070
|
||||
0xc00c9078 0x00000078 0xc00c9034 R_HEX_B32_PCREL_X test_sym - 0xc00c9078
|
||||
0xc00c907c 0x0000007c 0xc00c9034 R_HEX_B15_PCREL_X test_sym - 0xc00c9078
|
||||
0xc00c9080 0x00000080 0xc00c9034 R_HEX_B32_PCREL_X test_sym - 0xc00c9080
|
||||
0xc00c9084 0x00000084 0xc00c9034 R_HEX_B7_PCREL_X test_sym - 0xc00c9080
|
||||
0xc00c9088 0x00000088 0xc00c9034 R_HEX_PLT_B22_PCREL test_sym
|
||||
0xc00c908c 0x0000008c 0xc00c9034 R_HEX_B32_PCREL_X test_sym - 0xc00c908c
|
||||
0xc00c9090 0x00000090 0xc00c9034 R_HEX_6_PCREL_X test_sym - 0xc00c908c
|
||||
0xc00c9094 0x00000094 0xc00c9034 R_HEX_DTPREL_32_6_X test_sym
|
||||
0xc00c9098 0x00000098 0xc00c9034 R_HEX_DTPREL_16_X test_sym
|
||||
0xc00c909c 0x0000009c 0xc00c9034 R_HEX_DTPREL_32_6_X test_sym
|
||||
0xc00c90a0 0x000000a0 0xc00c9034 R_HEX_DTPREL_16_X test_sym
|
||||
0xc00c90a4 0x000000a4 0xc00c9034 R_HEX_DTPREL_32_6_X test_sym
|
||||
0xc00c90a8 0x000000a8 0xc00c9034 R_HEX_DTPREL_11_X test_sym
|
||||
0xc00c90ac 0x000000ac 0xc00c9034 R_HEX_32 test_sym
|
||||
0xc00c90b0 0x000000b0 0xc00c9034 R_HEX_16 test_sym
|
||||
0xc00c90b4 0x000000b4 0xc00c9034 R_HEX_8 test_sym
|
||||
0xc00c90b8 0x000000b8 0xc00c9034 R_HEX_32_PCREL test_sym - 0xc00c90b8
|
||||
EOF
|
||||
RUN
|
||||
|
||||
|
||||
NAME=ir - relocs patching
|
||||
FILE=bins/mdt/bin-mdt/load-test.mdt
|
||||
CMDS=<<EOF
|
||||
|
||||
# This should give the same (relative) result as the reloc tests in db/analysis/hexagon.
|
||||
# The load-bin.b04 is the same binary as used for the reloc tests there.
|
||||
|
||||
e plugins.hexagon.imm.sign=false
|
||||
echo "==== Print instructions ===="
|
||||
s section.load_test.b04.text
|
||||
pi 30
|
||||
echo "==== Print patched bytes ===="
|
||||
s loc.r_hex_32
|
||||
px 16~[1-8]
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
==== Print instructions ====
|
||||
[ nop
|
||||
[ nop
|
||||
[ nop
|
||||
[ nop
|
||||
[ jump 0xc00c9038
|
||||
[ if (P0) jump:nt loc.test_sym
|
||||
[ loop1(loc.test_sym,#0x0)
|
||||
[ R0.l = #0x9034
|
||||
[ R0.h = #0xc00c
|
||||
[ if (R0!=#0) jump:nt loc.test_sym
|
||||
[ R0 = #0x0 ; jump loc.test_sym
|
||||
/ immext(##0xffffffc0)
|
||||
\ jump loc.test_sym
|
||||
/ immext(##segment.load_test.b04)
|
||||
\ R0 = ##loc.test_sym
|
||||
/ immext(##0xffffffc0)
|
||||
\ jump loc.test_sym
|
||||
/ immext(##0xffffff80)
|
||||
\ if (P0) jump:nt loc.test_sym
|
||||
/ immext(##0xffffff80)
|
||||
\ loop1(loc.test_sym,#0x0)
|
||||
[ jump loc.test_sym
|
||||
/ immext(##0xffffff80)
|
||||
\ R0 = ##0xffffffa8 ; R1 = R1
|
||||
/ immext(##segment.load_test.b04)
|
||||
\ R0 = ##loc.test_sym
|
||||
/ immext(##segment.load_test.b04)
|
||||
\ R0 = ##loc.test_sym
|
||||
/ immext(##segment.load_test.b04)
|
||||
\ R0 = memw(R0+##loc.test_sym)
|
||||
==== Print patched bytes ====
|
||||
offset - 0 1 2 3 4 5
|
||||
3490 0cc0 3490 0000 3400 0000 7cff ffff
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=is - symbols count
|
||||
FILE=bins/mdt/bin-mdt/load-test.mdt
|
||||
CMDS=<<EOF
|
||||
|
||||
# The binaries in the image are test/bins/analysis/hexagon-hello-loop
|
||||
# and test/bins/elf/hexagon/relocs.
|
||||
|
||||
# The numbers of symbols should always match the sum.
|
||||
# So it can be cross-checked.
|
||||
echo "==== symbols count hexagon-hello-loop + relocs. Expected 519 + 22 ===="
|
||||
isq~?
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
==== symbols count hexagon-hello-loop + relocs. Expected 519 + 22 ====
|
||||
541
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=is - symbols vaddr
|
||||
FILE=bins/mdt/bin-mdt/load-test.mdt
|
||||
CMDS=<<EOF
|
||||
|
||||
# The binaries in the image are test/bins/analysis/hexagon-hello-loop
|
||||
# and test/bins/elf/hexagon/relocs.
|
||||
|
||||
e plugins.hexagon.imm.sign=false
|
||||
echo "==== symbols vaddr correct hexagon-hello-loop ===="
|
||||
is~fputs
|
||||
is~.CONST_4197D78400000000
|
||||
echo "==== symbols vaddr correct relocs ===="
|
||||
is~test_sym
|
||||
is~r_hex_b15_pcrel
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
==== symbols vaddr correct hexagon-hello-loop ====
|
||||
425 0x0000a2d0 0x005592d0 GLOBAL FUNC 244 fputs
|
||||
61 0x00001000 0x00550000 LOCAL FILE 0 fputs.c
|
||||
549 0x0000e080 0x0055d080 GLOBAL NOTYPE 0 .CONST_4197D78400000000
|
||||
==== symbols vaddr correct relocs ====
|
||||
22 0x00000034 0xc00c9034 GLOBAL NOTYPE 0 test_sym
|
||||
9 0x00000078 0xc00c9078 LOCAL NOTYPE 0 r_hex_b15_pcrel_x
|
||||
8 0x00000048 0xc00c9048 LOCAL NOTYPE 0 r_hex_b15_pcrel
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=is - symbols assembly
|
||||
FILE=bins/mdt/bin-mdt/load-test.mdt
|
||||
CMDS=<<EOF
|
||||
|
||||
# The binaries in the image are test/bins/analysis/hexagon-hello-loop
|
||||
# and test/bins/elf/hexagon/relocs.
|
||||
# Note that hexagon-hello-loop doesn't have relocations.
|
||||
|
||||
e plugins.hexagon.imm.sign=false
|
||||
echo "==== hexagon-hello-loop ===="
|
||||
# One for each executable section.
|
||||
echo "== .init"
|
||||
pi 10 @ sym..init
|
||||
echo "== .text"
|
||||
pi 10 @ sym._Putfld
|
||||
echo "== .fini"
|
||||
pi 10 @ sym._fini
|
||||
echo "==== relocs ===="
|
||||
echo "== loc.r_hex_b7_pcrel_x"
|
||||
pi 10 @ loc.r_hex_b7_pcrel_x
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
==== hexagon-hello-loop ====
|
||||
== .init
|
||||
? allocframe(SP,#0x8):raw
|
||||
[ memw(SP+##0x0) = R27
|
||||
[ R0.h = #0x0
|
||||
[ R0.l = #0xb760
|
||||
[ R1 = memw(R0+##0x0)
|
||||
[ P0 = cmp.eq(R1,#0x0); if (P0.new) jump:nt 0x55402c
|
||||
/ R2 = ##0x0
|
||||
\ R3 = ##0x0 ; R1 = ##0x0
|
||||
[ R28.h = #0x0
|
||||
[ R28.l = #0x5f80
|
||||
== .text
|
||||
/ R4 = add(R2,##0xffffffdb)
|
||||
| R16 = R0
|
||||
\ memd(R29+#0xfffffff0) = R17:16 ; allocframe(#0x18)
|
||||
[ P0 = cmp.gtu(R4,##0x53)
|
||||
/ if (P0) jump:nt 0x556638
|
||||
\ memd(SP+##0x8) = R19:18
|
||||
/ immext(##0xb100)
|
||||
\ R4 = memw(R4<<#0x2+##0xb130)
|
||||
[ jumpr R4
|
||||
/ R4 = memw(R16+##0x2c)
|
||||
== .fini
|
||||
[ allocframe(SP,#0x8):raw
|
||||
[ memw(SP+##0x0) = R27
|
||||
[ R27.h = #0x0
|
||||
[ R27.l = #0xc718
|
||||
[ R27 = add(R27,##0x4)
|
||||
[ R0 = memw(R27+##0x0)
|
||||
[ P0 = cmp.eq(R0,#0x0); if (P0.new) jump:nt 0x55b0c4
|
||||
[ callr R0
|
||||
[ jump 0x55b0b0
|
||||
/ R27 = memw(SP+##0x0)
|
||||
==== relocs ====
|
||||
== loc.r_hex_b7_pcrel_x
|
||||
/ immext(##0xffffff80)
|
||||
\ loop1(loc.test_sym,#0x0)
|
||||
[ jump loc.test_sym
|
||||
/ immext(##0xffffff80)
|
||||
\ R0 = ##0xffffffa8 ; R1 = R1
|
||||
/ immext(##segment.load_test.b04)
|
||||
\ R0 = ##loc.test_sym
|
||||
/ immext(##segment.load_test.b04)
|
||||
\ R0 = ##loc.test_sym
|
||||
/ immext(##segment.load_test.b04)
|
||||
EOF
|
||||
RUN
|
||||
|
|
@ -42,5 +42,8 @@ EXPECT_ERR=<<EOF
|
|||
WARNING: Neither hash nor gnu_hash exist. Falling back to heuristics for deducing the number of dynamic symbols...
|
||||
WARNING: Neither hash nor gnu_hash exist. Falling back to heuristics for deducing the number of dynamic symbols...
|
||||
WARNING: Neither hash nor gnu_hash exist. Falling back to heuristics for deducing the number of dynamic symbols...
|
||||
WARNING: Neither hash nor gnu_hash exist. Falling back to heuristics for deducing the number of dynamic symbols...
|
||||
WARNING: Neither hash nor gnu_hash exist. Falling back to heuristics for deducing the number of dynamic symbols...
|
||||
WARNING: Neither hash nor gnu_hash exist. Falling back to heuristics for deducing the number of dynamic symbols...
|
||||
EOF
|
||||
RUN
|
||||
|
|
|
|||
Loading…
Reference in a new issue