"""One loaded NSO module as the generator and its analyses see it: image at a guest base, .text as words, function seeds, and bounded-cache instruction lifting. The function seeds and the .eh_frame_hdr reader are adapted from LAS Recomp tools/analyze.py and tools/gen.py @ da35b83 (MIT, Copyright (c) 2026 Peiikai). """ import struct from collections import OrderedDict import numpy as np from lifter import Insn, Lifter, Unsupported, hx from nso import Nso, R_AARCH64_RELATIVE LIFT_CACHE = 1 << 16 # lifted instructions kept per module; analyses revisit nearby addresses only def _read_encoded(img, off, enc, pc_base): fmt, app = enc & 0x0F, enc & 0x70 if fmt == 0x0B: # sdata4 v, size = struct.unpack_from('> 2]) def read(self, a, n, signed=False): """Little-endian integer of n bytes at guest address a (unrelocated image).""" o = a - self.base return int.from_bytes(self.image[o:o + n], 'little', signed=signed) # ---- lifting def lift(self, a): """Lifted instruction at a; Unsupported becomes an rt_udf trap.""" ins = self._cache.get(a) if ins is not None: self._cache.move_to_end(a) return ins try: ins = self.lifter.lift(self.word(a), a) except Unsupported as e: ins = Insn('trap', f'rt_udf(c, {hx(a)}); /* {e} */') self._cache[a] = ins if len(self._cache) > LIFT_CACHE: self._cache.popitem(last=False) return ins # ---- function seeds def eh_frame_functions(self): """Function starts from .eh_frame_hdr's binary search table (absolute addresses).""" img, hdr = self.image, self.nso.eh_start if not hdr or hdr >= len(img): return [] _, ptr_enc, cnt_enc, tbl_enc = img[hdr:hdr + 4] off = hdr + 4 _, sz = _read_encoded(img, off, ptr_enc, hdr) off += sz count, sz = _read_encoded(img, off, cnt_enc, hdr) off += sz starts = [] for _ in range(count): loc, sz = _read_encoded(img, off, tbl_enc, hdr) off += sz _, sz = _read_encoded(img, off, tbl_enc, hdr) off += sz starts.append(self.base + loc) return starts def seeds(self): """Known function starts: .eh_frame_hdr, exported FUNC symbols, R_AARCH64_RELATIVE targets in .text.""" starts = set(self.eh_frame_functions()) for _, info, shndx, value, _ in self.nso.syms: if shndx and (info & 0xF) == 2: starts.add(self.base + value) for _, typ, _, add in self.nso.relocs: if typ == R_AARCH64_RELATIVE: starts.add(self.base + add) return sorted(a for a in starts if self.in_text(a))