"""frame.unity_tls_key_pool (GitHub #198): libunity.so's thread-key imports go to libfp_tkeys.so.""" import struct import zipfile import pytest from elftools.elf.elffile import ELFFile from frameport.analysis import elf from frameport.apk.workspace import ApkWorkspace from frameport.core.events import Reporter from frameport.core.models import Analysis from frameport.patches import base from frameport.patches.frame import artifact from frameport.patches.frame.unity_tls_key_pool import HELPER, RENAMES from frameport.validate.triage import triage # the helper itself imports the four pthread functions (with bionic's LIBC version): a real arm64 fixture LIB = artifact("arm64-v8a", HELPER) def _imports(data: bytes) -> dict[str, int]: """Undefined dynamic symbol -> its .gnu.version index.""" e = ELFFile(__import__("io").BytesIO(data)) syms = list(e.get_section_by_name(".dynsym").iter_symbols()) vs = e.get_section_by_name(".gnu.version") raw = data[vs["sh_offset"]:vs["sh_offset"] + vs["sh_size"]] return {s.name: struct.unpack_from(" 1 for n in RENAMES) # versioned (LIBC) imports out, renamed = elf.rename_imports(LIB, RENAMES) assert renamed == sorted(RENAMES) assert len(out) == len(LIB) after = _imports(out) for old, new in RENAMES.items(): assert old not in after and after[new] == 1 # renamed, version made global assert {n: v for n, v in after.items() if n not in RENAMES.values()} == \ {n: v for n, v in before.items() if n not in RENAMES} # other imports untouched assert elf.dyn_symbols(out, defined=True) == elf.dyn_symbols(LIB, defined=True) again, renamed = elf.rename_imports(out, RENAMES) assert renamed == [] and again == out def test_rename_imports_refuses_longer_and_shared_names(): with pytest.raises(ValueError): elf.rename_imports(LIB, {"free": "free_longer"}) # point another import's name into the tail of pthread_key_create's string ("create"): renaming must refuse e = ELFFile(__import__("io").BytesIO(LIB)) dynsym = e.get_section_by_name(".dynsym") syms = list(dynsym.iter_symbols()) key = next(s for s in syms if s.name == "pthread_key_create") other = next(i for i, s in enumerate(syms) if s.name == "calloc") buf = bytearray(LIB) struct.pack_into("