#!/usr/bin/env python3 """ Focused search for RISC-V function prologues in GSP firmware. Only searches in executable code sections. """ import struct import json import os FWIMAGE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "gsp-570.144.fwimage.bin") OUTPUT_DIR = os.path.dirname(os.path.abspath(__file__)) BASE_ADDR = 0x04000000 # Executable sections from memory_blocks.json EXEC_SECTIONS = [ (0x04005000, 0x04006413), # .ga100_text (0x04007000, 0x04008413), # .tu10x_text (0x04009000, 0x04009073), # .bootstrap (0x0400a000, 0x0400cfff), # .ga100_resident_text (0x0400e000, 0x04010fff), # .tu10x_resident_text (0x04018000, 0x0401cfff), # .section_task_init_elf_text_instance (0x04c58000, 0x05b21fff), # .section_task_rm_elf_text_instance (MAIN - 15.5MB) ] def read_binary(path): with open(path, 'rb') as f: return f.read() def is_in_exec_section(addr, offset): """Check if address is in an executable section.""" for start, end in EXEC_SECTIONS: if start <= addr <= end: return True return False def find_prologues(data, base_addr): functions = [] # Pattern 1: Standard 32-bit prologue (addi sp, sp, -N; sd ra, offset(sp)) for i in range(0, len(data) - 8, 2): addr = base_addr + i if not is_in_exec_section(addr, i): continue if i + 8 <= len(data): inst1 = struct.unpack('> 7) & 0x1F rs1 = (inst1 >> 15) & 0x1F if rd == 2 and rs1 == 2: imm = (inst1 >> 20) & 0xFFF if imm >= 0x800: imm = imm - 0x1000 # sd ra, offset(sp) (opcode=0x23, funct3=3, rs1=2, rs2=1) if (inst2 & 0x707F) == 0x23: funct3 = (inst2 >> 12) & 0x7 rs1_2 = (inst2 >> 15) & 0x1F rs2 = (inst2 >> 20) & 0x1F if funct3 == 3 and rs1_2 == 2 and rs2 == 1: functions.append({ "address": f"0x{addr:08x}", "offset": i, "stack_size": -imm, "pattern": "std_prologue" }) continue # Pattern 2: Compressed prologue (c.addi sp, sp, imm; c.sd ra, offset(sp)) for i in range(0, len(data) - 4, 2): addr = base_addr + i if not is_in_exec_section(addr, i): continue if i + 4 <= len(data): inst1 = struct.unpack('> 7) & 0x7 if rd_prime == 2: # c.sd ra, offset(sp) if (inst2 & 0xE003) == 0xE000: rs1_prime = (inst2 >> 7) & 0x7 rs2_prime = (inst2 >> 10) & 0x7 if rs1_prime == 2 and rs2_prime == 1: functions.append({ "address": f"0x{addr:08x}", "offset": i, "pattern": "compressed_prologue" }) continue # Pattern 3: Full prologue with s0 (addi sp, sp, -N; sd ra, N-8(sp); sd s0, N-16(sp); addi s0, sp, N) for i in range(0, len(data) - 16, 2): addr = base_addr + i if not is_in_exec_section(addr, i): continue if i + 16 <= len(data): inst1 = struct.unpack('> 7) & 0x1F rs1 = (inst1 >> 15) & 0x1F if rd == 2 and rs1 == 2: imm = (inst1 >> 20) & 0xFFF if imm >= 0x800: imm = imm - 0x1000 # sd ra, N-8(sp) if (inst2 & 0x707F) == 0x23: funct3 = (inst2 >> 12) & 0x7 rs1_2 = (inst2 >> 15) & 0x1F rs2 = (inst2 >> 20) & 0x1F if funct3 == 3 and rs1_2 == 2 and rs2 == 1: # sd s0, N-16(sp) if (inst3 & 0x707F) == 0x23: funct3_3 = (inst3 >> 12) & 0x7 rs1_3 = (inst3 >> 15) & 0x1F rs2_3 = (inst3 >> 20) & 0x1F if funct3_3 == 3 and rs1_3 == 2 and rs2_3 == 8: # s0 = x8 # addi s0, sp, N if (inst4 & 0x707F) == 0x0013: rd4 = (inst4 >> 7) & 0x1F rs1_4 = (inst4 >> 15) & 0x1F if rd4 == 8 and rs1_4 == 2: functions.append({ "address": f"0x{addr:08x}", "offset": i, "stack_size": -imm, "pattern": "full_prologue" }) continue return functions def main(): print(f"Reading firmware binary: {FWIMAGE_PATH}") data = read_binary(FWIMAGE_PATH) print(f"Binary size: {len(data)} bytes") print("Searching for function prologues in executable sections...") functions = find_prologues(data, BASE_ADDR) print(f"Found {len(functions)} potential functions") # Deduplicate by address seen = set() unique = [] for f in functions: if f["address"] not in seen: seen.add(f["address"]) unique.append(f) # Sort by address unique.sort(key=lambda x: x["offset"]) print(f"Unique functions: {len(unique)}") # Save results with open(f"{OUTPUT_DIR}/found_prologues.json", "w") as f: json.dump(unique, f, indent=2) with open(f"{OUTPUT_DIR}/prologue_addresses.txt", "w") as f: for func in unique: ss = func.get("stack_size", "?") f.write(f"{func['address']} stack_size={ss} pattern={func['pattern']}\n") print(f"Results saved to {OUTPUT_DIR}/found_prologues.json") # Group by pattern by_pattern = {} for func in unique: p = func["pattern"] if p not in by_pattern: by_pattern[p] = [] by_pattern[p].append(func) for pattern, funcs in by_pattern.items(): print(f"\n{pattern}: {len(funcs)} functions") for func in funcs[:20]: ss = func.get("stack_size", "?") print(f" {func['address']} stack_size={ss}") if len(funcs) > 20: print(f" ... and {len(funcs) - 20} more") if __name__ == "__main__": main()