-
Notifications
You must be signed in to change notification settings - Fork 2
Load app metadata from companion app_<target>.json file for Rust binaries #61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
+430
−0
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,12 @@ | ||
| import json | ||
| import logging | ||
| from argparse import ArgumentParser | ||
| from dataclasses import asdict, dataclass | ||
| from elftools.elf.elffile import ELFFile | ||
| from pathlib import Path | ||
| from typing import Optional, Union | ||
|
|
||
| from ledgered.devices import Devices | ||
| from ledgered.serializers import Jsonable | ||
|
|
||
| LEDGER_PREFIX = "ledger." | ||
|
|
@@ -46,10 +48,122 @@ def __init__(self, binary_path: Union[str, Path]): | |
| } | ||
| self._sections = Sections(**sections) | ||
|
|
||
| # Rust apps store app_name/app_version/app_flags in companion JSON file | ||
| if self.is_rust_app: | ||
| self._load_metadata_from_json() | ||
|
|
||
| def _load_metadata_from_json(self) -> None: | ||
| """Load app metadata from companion app_<target>.json file. | ||
|
|
||
| Rust applications don't embed app_name, app_version, and app_flags in the ELF. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not true anymore with this PR. PS. Also, all these data can be retrieved from |
||
| Instead, these are stored in a JSON file named app_<target>.json in the same directory. | ||
| This method is called only for Rust applications. | ||
|
|
||
| Note: The target name in the ELF may differ from the JSON filename. | ||
| For example, nanos2 -> nanosplus, nanos+ -> nanosplus. | ||
| """ | ||
| target = self._sections.target | ||
| if not target: | ||
| logging.warning( | ||
| "Rust app detected but no target found, cannot locate companion JSON file" | ||
| ) | ||
| return | ||
|
|
||
| # Try multiple naming patterns to find the JSON file | ||
| json_path = self._find_json_file(target) | ||
| if not json_path: | ||
| logging.warning( | ||
| "Rust app detected but companion JSON file not found for target '%s' in %s", | ||
| target, | ||
| self._path.parent, | ||
| ) | ||
| return | ||
|
|
||
| try: | ||
| logging.info("Loading Rust app metadata from %s", json_path) | ||
| with json_path.open("r") as f: | ||
| data = json.load(f) | ||
|
|
||
| if "name" in data: | ||
| self._sections.app_name = data["name"] | ||
| logging.debug("Loaded app_name: %s", self._sections.app_name) | ||
|
|
||
| if "version" in data: | ||
| self._sections.app_version = data["version"] | ||
| logging.debug("Loaded app_version: %s", self._sections.app_version) | ||
|
|
||
| if "flags" in data: | ||
| self._sections.app_flags = data["flags"] | ||
| logging.debug("Loaded app_flags: %s", self._sections.app_flags) | ||
|
|
||
| except (json.JSONDecodeError, OSError) as e: | ||
| logging.error("Failed to load companion JSON file %s: %s", json_path, e) | ||
|
|
||
| def _find_json_file(self, target: str) -> Optional[Path]: | ||
| """Find the companion JSON file using multiple naming patterns. | ||
|
|
||
| Tries different naming conventions: | ||
| 1. Exact target name (e.g., nanos2 -> app_nanos2.json) | ||
| 2. Canonical device name (e.g., nanos2 -> app_nanosp.json) | ||
| 3. All device aliases (e.g., nanos+, nanosplus for NanoS+) | ||
|
|
||
| Args: | ||
| target: The target name from the ELF binary | ||
|
|
||
| Returns: | ||
| Path to the JSON file if found, None otherwise | ||
| """ | ||
| candidates = [target] # Start with exact target name | ||
|
|
||
| try: | ||
| device = Devices.get_by_name(target) | ||
| # Add canonical device name | ||
| candidates.append(device.name) | ||
| # Add all known aliases | ||
| candidates.extend(device.names) | ||
| except KeyError: | ||
| logging.debug("Unknown device '%s', trying exact name only", target) | ||
|
|
||
| # Try all candidates | ||
| for candidate in candidates: | ||
| json_path = self._path.parent / f"app_{candidate}.json" | ||
| if json_path.exists(): | ||
| logging.debug("Found JSON file with pattern '%s': %s", candidate, json_path) | ||
| return json_path | ||
|
|
||
| return None | ||
|
|
||
| def _normalize_target_name(self, target: str) -> str: | ||
| """Normalize target name to match JSON filename conventions. | ||
|
|
||
| Device names can vary (e.g., nanos2, nanos+, nanosplus all refer to the same device). | ||
| The JSON files use a canonical naming (nanosplus, flex, stax, etc.). | ||
|
|
||
| Args: | ||
| target: The target name from the ELF binary | ||
|
|
||
| Returns: | ||
| The normalized target name for JSON file lookup | ||
| """ | ||
| try: | ||
| device = Devices.get_by_name(target) | ||
| return device.name | ||
| except KeyError: | ||
| # If device is unknown, return the original target name | ||
| logging.debug("Unknown device '%s', using target name as-is", target) | ||
| return target | ||
|
|
||
| @property | ||
| def sections(self) -> Sections: | ||
| return self._sections | ||
|
|
||
| @property | ||
| def is_rust_app(self) -> bool: | ||
| """Returns True if this is a Rust application.""" | ||
| return ( | ||
| self._sections.rust_sdk_name is not None or self._sections.rust_sdk_version is not None | ||
| ) | ||
|
|
||
|
|
||
| def set_parser() -> ArgumentParser: | ||
| parser = ArgumentParser( | ||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.