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:
Rot127 2025-06-11 07:28:23 -05:00 committed by Rot127
parent 28bd9cfa24
commit 1f6c6147ec
10 changed files with 1259 additions and 3 deletions

View file

@ -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"

View file

@ -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
View 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(&section->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
View 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

View file

@ -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
View 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

View file

@ -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
View 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

View file

@ -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