#!/usr/bin/env python3 import hashlib import os import struct import subprocess import tempfile from collections import defaultdict from dataclasses import dataclass from elftools.elf.elffile import ELFFile from elftools.elf.enums import ENUM_RELOC_TYPE_ARM from elftools.elf.relocation import RelocationSection from elftools.elf.sections import SymbolTableSection from fbt.sdk.hashes import gnu_sym_hash from flipper.app import App VERSION = 1 @dataclass class RelData: section: int section_value: int type: int offset: int name: str @dataclass(frozen=True) class UniqueRelData: section: int section_value: int type: int name: str @dataclass class RelSection: name: str oringinal_name: str data: dict[UniqueRelData, list[int]] def serialize_relsection_data(data: dict[UniqueRelData, list[int]]) -> bytes: result = struct.pack(" 0: result += struct.pack("> 8) & 0xFF, (offset >> 16) & 0xFF ) return result class Main(App): def init(self): self.parser.add_argument("fap_src_path", help="App file to upload") self.parser.add_argument("objcopy_path", help="Objcopy path") self.parser.set_defaults(func=self.process) def process(self): fap_path = self.args.fap_src_path objcopy_path = self.args.objcopy_path sections: list[RelSection] = [] with open(fap_path, "rb") as f: elf_file = ELFFile(f) relocation_sections: list[RelocationSection] = [] symtab_section: SymbolTableSection | None = None for section in elf_file.iter_sections(): if isinstance(section, RelocationSection): relocation_sections.append(section) if isinstance(section, SymbolTableSection): symtab_section = section if not symtab_section: self.logger.error("No symbol table found") return 1 if not relocation_sections: self.logger.info("No relocation sections found") return 0 for section in relocation_sections: section_relocations: list[RelData] = [] for relocation in section.iter_relocations(): symbol_id: int = relocation.entry["r_info_sym"] offset: int = relocation.entry["r_offset"] type: int = relocation.entry["r_info_type"] symbol = symtab_section.get_symbol(symbol_id) section_index: int = symbol["st_shndx"] section_value: int = symbol["st_value"] if section_index == "SHN_UNDEF": section_index = 0 section_relocations.append( RelData(section_index, section_value, type, offset, symbol.name) ) unique_relocations: dict[UniqueRelData, list[int]] = defaultdict(list) for relocation in section_relocations: unique = UniqueRelData( relocation.section, relocation.section_value, relocation.type, relocation.name, ) unique_relocations[unique].append(relocation.offset) section_name = section.name if section_name.startswith(".rel"): section_name = ".fast.rel" + section_name[4:] else: self.logger.error( "Unknown relocation section name: %s", section_name ) return 1 sections.append( RelSection(section_name, section.name, unique_relocations) ) with tempfile.TemporaryDirectory() as temp_dir: for section in sections: data = serialize_relsection_data(section.data) hash_name = hashlib.md5(section.name.encode()).hexdigest() filename = f"{temp_dir}/{hash_name}.bin" if os.path.isfile(filename): self.logger.error(f"File {filename} already exists") return 1 with open(filename, "wb") as f: f.write(data) exit_code = subprocess.run( [ objcopy_path, "--add-section", f"{section.name}={filename}", fap_path, ], check=True, ) if exit_code.returncode != 0: self.logger.error("objcopy failed") return 1 return 0 if __name__ == "__main__": Main()()