diff --git a/Pipfile b/Pipfile index 9d2de6ff..c5aaf9cf 100644 --- a/Pipfile +++ b/Pipfile @@ -15,6 +15,7 @@ pillow = ">=8.0.0,<11.0.0" pyelftools = ">=0.27,<1.0" pyqt5 = ">=5.15.2,<6.0.0" requests = ">=2.25.1,<3.0.0" +ledgered = ">=0.6.2" [requires] python_version = "3.9" diff --git a/pyproject.toml b/pyproject.toml index 06252c69..90124c0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,7 @@ dependencies = [ "pyelftools>=0.27,<1.0", "pyqt5>=5.15.2,<6.0.0", "requests>=2.25.1,<3.0.0", + "ledgered>=0.6.2", ] dynamic = ["version"] diff --git a/speculos/main.py b/speculos/main.py index 626a1571..4cb791df 100644 --- a/speculos/main.py +++ b/speculos/main.py @@ -16,6 +16,7 @@ import threading import pkg_resources from elftools.elf.elffile import ELFFile +from ledgered.binary import LedgerBinaryApp from mnemonic import mnemonic from typing import Optional, Type @@ -48,28 +49,6 @@ def set_pdeath(sig): libc.prctl(PR_SET_PDEATHSIG, sig) -ELF_METADATA_SECTIONS = [ - "target", - "app_name", - "app_version", - "api_level" -] - - -def get_elf_ledger_metadata(app_path): - with open(app_path, 'rb') as fp: - elf = ELFFile(fp) - metadata = {} - for section_name in ELF_METADATA_SECTIONS: - section = elf.get_section_by_name(f"ledger.{section_name}") - if section: - metadata[section_name] = section.data().decode("utf-8").strip() - if section_name == "target": - if metadata[section_name] == "nanos2": - metadata[section_name] = "nanosp" - return metadata - - def get_elf_infos(app_path): with open(app_path, 'rb') as fp: elf = ELFFile(fp) @@ -140,21 +119,6 @@ def get_cx_infos(app_path): return sh_offset, sh_size, sh_load, cx_ram_size, cx_ram_load -def get_elf_fonts_size(app_path): - ''' - Check for C_bagl_fonts symbol to determine if the app use BAGL or NBGL syscalls. - If C_bagl_fonts is found, it means that this app uses BAGL (useful on NanoX and NanoSP) - ''' - with open(app_path, 'rb') as fp: - elf = ELFFile(fp) - symtab = elf.get_section_by_name('.symtab') - bagl_fonts_symbol = symtab.get_symbol_by_name('C_bagl_fonts') - if bagl_fonts_symbol is not None: - return bagl_fonts_symbol[0]['st_size'] - - return 0 - - def run_qemu(s1: socket.socket, s2: socket.socket, args: argparse.Namespace, use_bagl: bool) -> int: argv = ['qemu-arm-static'] @@ -335,24 +299,25 @@ def main(prog=None) -> int: # Init model and api_level if not specified from app elf metadata app_path = getattr(args, 'app.elf') - metadata = get_elf_ledger_metadata(app_path) + binary = LedgerBinaryApp(app_path) if not args.model: - if "target" not in metadata: + if binary.sections.target is None: logger.error("Device model not detected from elf. Then it must be specified") sys.exit(1) else: - args.model = metadata["target"] + args.model = "nanosp" if binary.sections.target == "nanos2" else binary.sections.target logger.warn(f"Device model detected from metadata: {args.model}") - # For Nano S and Blue, it can only be BAGL - if args.model == "nanos" or args.model == "blue": - use_bagl = True + # 'bagl' is the default value of the binary.sections.sdk_graphics. We need to + # manage the cases where it is NOT 'bagl' but the section does not exists yet + if args.model in ["stax", "flex"]: + use_bagl = False else: - use_bagl = get_elf_fonts_size(app_path) != 0 + use_bagl = binary.sections.sdk_graphics == "bagl" if not args.apiLevel: - if "api_level" in metadata: - args.apiLevel = metadata["api_level"] + if binary.sections.api_level is not None: + args.apiLevel = binary.sections.api_level logger.warn(f"Api level detected from metadata: {args.apiLevel}") # Check args.apiLevel, 0 is an invalid value @@ -362,8 +327,8 @@ def main(prog=None) -> int: # Set SPECULOS_DETECTED_APPNAME env variable for proper emulation. # See sys_os_registry_get_current_app_tag() for corresponding usage. - app_name = metadata.get("app_name") - app_version = metadata.get("app_version") + app_name = binary.sections.app_name + app_version = binary.sections.app_version if app_name and app_version: os.environ["SPECULOS_DETECTED_APPNAME"] = f"{app_name}:{app_version}" @@ -376,8 +341,7 @@ def main(prog=None) -> int: lib_name = None lib_path = lib_arg - metadata = get_elf_ledger_metadata(lib_path) - elf_lib_name = metadata.get("app_name", None) + elf_lib_name = LedgerBinaryApp(lib_path).sections.app_name if lib_name is None: if elf_lib_name is None: @@ -396,14 +360,14 @@ def main(prog=None) -> int: # Check model and api_level against all lib elf metadata for path in [app_path] + [x.split(":")[1] for x in args.library]: - metadata = get_elf_ledger_metadata(path) + binary = LedgerBinaryApp(path) - elf_model = metadata.get("target", args.model) + elf_model = ("nanosp" if binary.sections.target == "nanos2" else binary.sections.target) or args.model if args.model != elf_model: logger.error(f"Invalid model in {path} ({elf_model} vs {args.model})") sys.exit(1) - elf_api_level = metadata.get("api_level", "0") + elf_api_level = binary.sections.api_level or "0" # Check args.apiLevel against elf api level. If elf api level == 0 (SDK master # reserved value) ignore it. if elf_api_level != "0" and args.apiLevel != elf_api_level: