Skip to content

Commit

Permalink
[add] Using 'ledgered' to extract metadata from the application elf
Browse files Browse the repository at this point in the history
  • Loading branch information
lpascal-ledger committed Mar 27, 2024
1 parent 9ac0d83 commit ff9c635
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 53 deletions.
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
70 changes: 17 additions & 53 deletions speculos/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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']

Expand Down Expand Up @@ -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
Expand All @@ -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}"

Expand All @@ -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:
Expand All @@ -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:
Expand Down

0 comments on commit ff9c635

Please sign in to comment.