feat(tools): add tool for frozen_mpy bytecode analysis

pull/2633/head
grdddj 2 years ago committed by matejcik
parent b95cb34e0c
commit 579ee06b6f

@ -0,0 +1,148 @@
"""
Translate bytecode instructions in frozen_mpy.c to human readable form.
"""
from __future__ import annotations
from pathlib import Path
HERE = Path(__file__).parent
CORE_DIR = HERE.parent
FROZEN_MPY_FILE = CORE_DIR / "build/firmware/frozen_mpy.c"
NEW_FILE = str(FROZEN_MPY_FILE) + "_translated"
# Taken from core/vendor/micropython/py/bc0.h
OPCODES: dict[str, str] = {
"0x10": "LOAD QSTR const",
"0x11": "LOAD QSTR name",
"0x12": "LOAD QSTR global",
"0x13": "LOAD QSTR attribute",
"0x14": "LOAD QSTR method",
"0x15": "LOAD QSTR super method",
"0x16": "STORE QSTR name",
"0x17": "STORE QSTR global",
"0x18": "STORE QSTR attribute",
"0x19": "DELETE QSTR name",
"0x1a": "DELETE QSTR global",
"0x1b": "IMPORT QSTR name",
"0x1c": "IMPORT QSTR from",
"0x20": "MAKE closure",
"0x21": "MAKE closure defargs",
"0x22": "LOAD small int",
"0x23": "LOAD obj ptr",
"0x24": "LOAD fast N uint",
"0x25": "LOAD deref uint",
"0x26": "STORE fast N uint",
"0x27": "STORE deref uint",
"0x28": "DELETE fast N uint",
"0x29": "DELETE deref uint",
"0x2a": "BUILD tuple",
"0x2b": "BUILD list",
"0x2c": "BUILD map",
"0x2d": "BUILD set",
"0x2e": "BUILD slice",
"0x2f": "STORE comp",
"0x30": "UNPACK sequence",
"0x31": "UNPACK ex",
"0x32": "MAKE function",
"0x33": "MAKE function defargs",
"0x34": "CALL function",
"0x35": "CALL function var kw",
"0x36": "CALL method",
"0x37": "CALL method var kw",
"0x40": "JUMP unwind",
"0x42": "JUMP",
"0x43": "JUMP pop if true",
"0x44": "JUMP pop if false",
"0x45": "JUMP if true or pop",
"0x46": "JUMP if false or pop",
"0x47": "SETUP with",
"0x48": "SETUP except",
"0x49": "SETUP finally",
"0x4a": "POP block",
"0x50": "LOAD False",
"0x51": "LOAD None",
"0x52": "LOAD True",
"0x53": "LOAD null",
"0x54": "LOAD build class",
"0x55": "LOAD subscript",
"0x56": "STORE subscript",
"0x57": "DUP top",
"0x58": "DUP top two",
"0x59": "POP top",
"0x5a": "ROT two",
"0x5b": "ROT three",
"0x5c": "WITH cleanup",
"0x5d": "END finally",
"0x5e": "GET iter",
"0x5f": "GET iter stack",
"0x62": "STORE map",
"0x63": "RETURN value",
"0x64": "RAISE last",
"0x65": "RAISE obj",
"0x66": "RAISE from",
"0x67": "YIELD value",
"0x68": "YIELD from (AWAIT)",
"0x69": "IMPORT star",
"0xd7": "<",
"0xd8": ">",
"0xd9": "==",
"0xda": "<=",
"0xdb": ">=",
"0xdc": "!=",
"0xde": "is",
"0xef": "&",
"0xf0": "<<",
}
def main() -> None:
with open(FROZEN_MPY_FILE, "r") as f:
lines = f.readlines()
new_lines: list[str] = []
for line in lines:
first_byte = line.strip().split(",")[0]
# Check if it is a byte, otherwise skip
try:
num = int(first_byte, 16)
except ValueError:
new_lines.append(line)
continue
# Some OP codes have a value in themselves
if 0x70 <= num <= 0xAF:
# Small int
var = num - 0x10 - 0x70
val = f"INT {var}"
line = line.replace(first_byte, val, 1)
elif 0xB0 <= num <= 0xBF:
# Local variable loading
var = num - 0xB0
val = f"LOAD local {var}"
line = line.replace(first_byte, val, 1)
elif 0xC0 <= num <= 0xCF:
# Local variable storing
var = num - 0xC0
val = f"STORE local {var}"
line = line.replace(first_byte, val, 1)
else:
# Hardcoded OPcodes
if first_byte in OPCODES:
line = line.replace(first_byte, OPCODES[first_byte], 1)
else:
pass
# print(f"Unknown opcode: {first_byte}")
new_lines.append(line)
with open(NEW_FILE, "w") as f:
f.write("".join(new_lines))
print(f"Translated file saved as {NEW_FILE}")
if __name__ == "__main__":
main()
Loading…
Cancel
Save