本文主要是介绍MicroPython字节码文件——mpy文件解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
参考文档:MicroPython .mpy files
前置注意点
- mpy文件的二进制文件是一种嵌套格式。先描述外层模块,然后描述子模块。
- 使用vint(变长编码)表示整数,当前字节最高位置1表示还有后续字节。
- 不同版本的MicroPython对应的mpy文件格式不一定兼容。
MicroPython项目提供了一个脚本mpy-tool.py来解析mpy文件的内容,所以想知道mpy文件的结构、具体包含了哪些信息,可以阅读下这个脚本源码。
You can inspect the contents of a .mpy file by using mpy-tool.py, for
example (run from the root of the main MicroPython repository):
$ ./tools/mpy-tool.py -xd myfile.mpy
可以自行下载阅读器(比如winHex),查看mpy文件的十六进制码。
mpy文件内容概览
全局的mpy文件内容构成如图,首先是4个字节的header,接着是2个vint,分别表示qstr和obj的数量。
Header
4个字节的头部,用于标识mpy文件和版本、CPU架构要求、小整型的位数。
qstr and constant objects tables
qstr表
在这个表中,每个qstr对象都由一个表示长度的vint和后续数据组成。
qstr unit
注意,mpy文件里的len是实际qstr长度的2倍。这是由于最低位用来判别是否为static qstr。比如字节码为 0a 416c 6963 6500,第一个字节0a最高位为0,表示无后续字节,则qstr长度为a/2 = 5。后面跟着5个字节为qstr的内容“Alice",第六个字节00表示字符串的结束。
def read_qstr(reader, segments):start_pos = reader.tell()ln = reader.read_uint()if ln & 1:# static qstrq = global_qstrs.get_by_index(ln >> 1)segments.append(MPYSegment(MPYSegment.META, q.str, start_pos, start_pos))return qln >>= 1start_pos = reader.tell()data = str_cons(reader.read_bytes(ln), "utf8")reader.read_byte() # read and discard null terminatorsegments.append(MPYSegment(MPYSegment.QSTR, data, start_pos, reader.tell()))return global_qstrs.add(data)
obj表 //todo: 待补充
def read_obj(reader, segments):obj_type = reader.read_byte()if obj_type == MP_PERSISTENT_OBJ_FUN_TABLE:return MPFunTable()elif obj_type == MP_PERSISTENT_OBJ_NONE:return Noneelif obj_type == MP_PERSISTENT_OBJ_FALSE:return Falseelif obj_type == MP_PERSISTENT_OBJ_TRUE:return Trueelif obj_type == MP_PERSISTENT_OBJ_ELLIPSIS:return Ellipsiselif obj_type == MP_PERSISTENT_OBJ_TUPLE:ln = reader.read_uint()return tuple(read_obj(reader, segments) for _ in range(ln))else:ln = reader.read_uint()start_pos = reader.tell()buf = reader.read_bytes(ln)if obj_type in (MP_PERSISTENT_OBJ_STR, MP_PERSISTENT_OBJ_BYTES):reader.read_byte() # read and discard null terminatorif obj_type == MP_PERSISTENT_OBJ_STR:obj = str_cons(buf, "utf8")if len(obj) < PERSISTENT_STR_INTERN_THRESHOLD:if not global_qstrs.find_by_str(obj):global_qstrs.add(obj)elif obj_type == MP_PERSISTENT_OBJ_BYTES:obj = bufelif obj_type == MP_PERSISTENT_OBJ_INT:obj = int(str_cons(buf, "ascii"), 10)elif obj_type == MP_PERSISTENT_OBJ_FLOAT:obj = float(str_cons(buf, "ascii"))elif obj_type == MP_PERSISTENT_OBJ_COMPLEX:obj = complex(str_cons(buf, "ascii"))else:raise MPYReadError(reader.filename, "corrupt .mpy file")segments.append(MPYSegment(MPYSegment.OBJ, obj, start_pos, reader.tell()))return obj
obj对象类型
MP_PERSISTENT_OBJ_FUN_TABLE = 0
MP_PERSISTENT_OBJ_NONE = 1
MP_PERSISTENT_OBJ_FALSE = 2
MP_PERSISTENT_OBJ_TRUE = 3
MP_PERSISTENT_OBJ_ELLIPSIS = 4
MP_PERSISTENT_OBJ_STR = 5
MP_PERSISTENT_OBJ_BYTES = 6
MP_PERSISTENT_OBJ_INT = 7
MP_PERSISTENT_OBJ_FLOAT = 8
MP_PERSISTENT_OBJ_COMPLEX = 9
MP_PERSISTENT_OBJ_TUPLE = 10
raw code header
字节码的头部是一个vint类型,最低两位表示代码类型
kind_len = reader.read_uint() # 读取一个vint
kind = (kind_len & 3) + MP_CODE_BYTECODE # 取低2位加上MP_CODE_BYTECODE个偏移量
4种代码类型定义如下
MP_CODE_BYTECODE = 2
MP_CODE_NATIVE_PY = 3
MP_CODE_NATIVE_VIPER = 4
MP_CODE_NATIVE_ASM = 5
低第三位表示有无子模块,0表示无子模块,1表示有子模块。
has_children = (kind_len >> 2) & 1
其余位表示长度,注意字节的最高位只用于表示有无后续字节,不算进长度表示的位中。
头部后面跟着的就是字节码的内容了。
示例
最后放个py源码和生成的mpy字节码供大家参考学习。
class Wallet:def __init__(self, owner_name, balance=0):self.owner_name = owner_nameself.balance = balancedef deposit(self, amount):if amount > 0:self.balance += amountprint(f"Deposited ${amount}. New balance: ${self.balance}")else:print("Invalid deposit amount.")def withdraw(self, amount):if amount > 0 and self.balance >= amount:self.balance -= amountprint(f"Withdrew ${amount}. New balance: ${self.balance}")else:print("Invalid or insufficient funds for withdrawal.")def transfer(self, recipient_wallet, amount):if self.balance >= amount and recipient_wallet is not self:if amount > 0:self.withdraw(amount)recipient_wallet.deposit(amount)print(f"Transferred ${amount} to {recipient_wallet.owner_name}.")else:print("Invalid transfer amount.")else:print("Insufficient funds or invalid recipient.")def check_balance(self):print(f"Current balance: ${self.balance}")# Example usage:
# Create two wallet instances
wallet1 = Wallet("Alice", 100)
wallet2 = Wallet("Bob", 50)# Deposit money into wallet1
wallet1.deposit(50)# Withdraw money from wallet1
wallet1.withdraw(20)# Transfer money from wallet1 to wallet2
wallet1.transfer(wallet2, 30)# Check balance of wallet1 and wallet2
wallet1.check_balance()
wallet2.check_balance()
00000000: 4d06 001f 1608 1c77 616c 6c65 745f 7465 M......wallet_te wallet_test.py
00000010: 7374 2e70 7900 0f0c 5761 6c6c 6574 000a st.py...Wallet.. <module> Wallet
00000020: 416c 6963 6500 0642 6f62 000e 6465 706f Alice..Bob..depo Alice Bob deposit
00000030: 7369 7400 1077 6974 6864 7261 7700 1074 sit..withdraw..t withdraw transfer
00000040: 7261 6e73 6665 7200 1a63 6865 636b 5f62 ransfer..check_b check_balance
00000050: 616c 616e 6365 0023 146f 776e 6572 5f6e alance.#.owner_n __init__ owner_name
00000060: 616d 6500 0e62 616c 616e 6365 0081 290e ame..balance..). balance format
00000070: 7761 6c6c 6574 3100 0e77 616c 6c65 7432 wallet1..wallet2 wallet1 wallet2
00000080: 002f 2d35 8213 0c61 6d6f 756e 7400 8177 ./-5...amount..w __name__ __module__ __qualname__ self amount print
00000090: 2072 6563 6970 6965 6e74 5f77 616c 6c65 recipient_walle recipient_wallet
000000a0: 7400 051f 4465 706f 7369 7465 6420 247b t...Deposited ${ Deposited ${}. New balance: ${}
000000b0: 7d2e 204e 6577 2062 616c 616e 6365 3a20 }. New balance:
000000c0: 247b 7d00 0517 496e 7661 6c69 6420 6465 ${}...Invalid de Invalid deposit amount.
000000d0: 706f 7369 7420 616d 6f75 6e74 2e00 051e posit amount....
000000e0: 5769 7468 6472 6577 2024 7b7d 2e20 4e65 Withdrew ${}. Ne Withdrew ${}. New balance: ${}
000000f0: 7720 6261 6c61 6e63 653a 2024 7b7d 0005 w balance: ${}..
00000100: 2d49 6e76 616c 6964 206f 7220 696e 7375 -Invalid or insu Invalid or insufficient funds for withdrawal.
00000110: 6666 6963 6965 6e74 2066 756e 6473 2066 fficient funds f
00000120: 6f72 2077 6974 6864 7261 7761 6c2e 0005 or withdrawal...
00000130: 1654 7261 6e73 6665 7272 6564 2024 7b7d .Transferred ${} Transferred ${} to {}.
00000140: 2074 6f20 7b7d 2e00 0518 496e 7661 6c69 to {}....Invali Invalid transfer amount.
00000150: 6420 7472 616e 7366 6572 2061 6d6f 756e d transfer amoun
00000160: 742e 0005 2849 6e73 7566 6669 6369 656e t...(Insufficien Insufficient funds or invalid recipient.
00000170: 7420 6675 6e64 7320 6f72 2069 6e76 616c t funds or inval
00000180: 6964 2072 6563 6970 6965 6e74 2e00 0514 id recipient....
00000190: 4375 7272 656e 7420 6261 6c61 6e63 653a Current balance: Current balance: ${}
000001a0: 2024 7b7d 0085 2418 1201 8923 2b6a 6968 ${}..$....#+jih <module>
000001b0: 6a27 5432 0010 0234 0216 0211 0210 0322 j'T2...4......."
000001c0: 8064 3402 160d 1102 1004 2232 3402 160e .d4......."24...
000001d0: 110d 1405 2232 3601 5911 0d14 0694 3601 ...."26.Y.....6.
000001e0: 5911 0d14 0711 0e9e 3602 5911 0d14 0836 Y.......6.Y....6
000001f0: 0059 110e 1408 3600 5951 6301 8274 0814 .Y....6.YQc..t.. Wallet
00000200: 0228 6820 8407 8407 840b 110f 1610 1002 .(h ............
00000210: 1611 802a 0153 3300 1609 3201 1605 3202 ...*.S3...2...2.
00000220: 1606 3203 1607 3204 1608 5163 0581 18a3 ..2...2...Qc.... __init__
00000230: 010c 0912 0a0b 4024 b1b0 180a b2b0 180b ......@$........
00000240: 5163 8310 3210 0512 1360 6025 2951 b180 Qc..2....``%)Q.. deposit
00000250: d844 5ab0 5713 0bb1 e55a 180b 1214 2300 .DZ.W....Z....#.
00000260: 140c b1b0 130b 3602 3401 5942 4712 1423 ......6.4.YBG..#
00000270: 0134 0159 5163 8348 3210 0612 1380 0d2c .4.YQc.H2......, withdraw
00000280: 2951 b180 d844 61b0 130b b1db 445a b057 )Q...Da.....DZ.W
00000290: 130b b1e6 5a18 0b12 1423 0214 0cb1 b013 ....Z....#......
000002a0: 0b36 0234 0159 4247 1214 2303 3401 5951 .6.4.YBG..#.4.YQ
000002b0: 6385 083b 1807 1215 1380 142d 2527 2751 c..;.......-%''Q transfer
000002c0: 49b0 130b b2db 4473 b1b0 ded3 446d b280 I.....Ds....Dm..
000002d0: d844 5fb0 1406 b236 0159 b114 05b2 3601 .D_....6.Y....6.
000002e0: 5912 1423 0414 0cb2 b113 0a36 0234 0159 Y..#.......6.4.Y
000002f0: 4247 1214 2305 3401 5942 4712 1423 0634 BG..#.4.YBG..#.4
00000300: 0159 5163 8130 2108 0812 801f 1214 2307 .YQc.0!.......#. check_balance
00000310: 140c b013 0b36 0134 0159 5163 .....6.4.YQc mpy_source_file: wallet_test.mpy
source_file: wallet_test.py
header: 4d:06:00:1f
qstr_table[22]:wallet_test.py<module>WalletAliceBobdepositwithdrawtransfercheck_balance__init__owner_namebalanceformatwallet1wallet2__name____module____qualname__selfamountprintrecipient_wallet
obj_table: ['Deposited ${}. New balance: ${}', 'Invalid deposit amount.', 'Withdrew ${}. New balance: ${}', 'Invalid or insufficient funds for withdrawal.', 'Transferred ${} to {}.', 'Invalid transfer amount.', 'Insufficient funds or invalid recipient.', 'Current balance: ${}']
simple_name: <module>raw bytecode: 84 18:12:01:89:23:2b:6a:69:68:6a:27:54:32:00:10:02:34:02:16:02:11:02:10:03:22:80:64:34:02:16:0d:11:02:10:04:22:32:34:02:16:0e:11:0d:14:05:22:32:36:01:59:11:0d:14:06:94:36:01:59:11:0d:14:07:11:0e:9e:36:02:59:11:0d:14:08:36:00:59:11:0e:14:08:36:00:59:51:63prelude: (4, 0, 0, 0, 0, 0)args: []line info: 89:23:2b:6a:69:68:6a:2754 LOAD_BUILD_CLASS 32:00 MAKE_FUNCTION 010:02 LOAD_CONST_STRING Wallet34:02 CALL_FUNCTION 216:02 STORE_NAME Wallet11:02 LOAD_NAME Wallet10:03 LOAD_CONST_STRING Alice22:80:64 LOAD_CONST_SMALL_INT 10034:02 CALL_FUNCTION 216:0d STORE_NAME wallet111:02 LOAD_NAME Wallet10:04 LOAD_CONST_STRING Bob22:32 LOAD_CONST_SMALL_INT 5034:02 CALL_FUNCTION 216:0e STORE_NAME wallet211:0d LOAD_NAME wallet114:05 LOAD_METHOD deposit22:32 LOAD_CONST_SMALL_INT 5036:01 CALL_METHOD 159 POP_TOP 11:0d LOAD_NAME wallet114:06 LOAD_METHOD withdraw94 LOAD_CONST_SMALL_INT 20 36:01 CALL_METHOD 159 POP_TOP 11:0d LOAD_NAME wallet114:07 LOAD_METHOD transfer11:0e LOAD_NAME wallet29e LOAD_CONST_SMALL_INT 30 36:02 CALL_METHOD 259 POP_TOP 11:0d LOAD_NAME wallet114:08 LOAD_METHOD check_balance36:00 CALL_METHOD 059 POP_TOP 11:0e LOAD_NAME wallet214:08 LOAD_METHOD check_balance36:00 CALL_METHOD 059 POP_TOP 51 LOAD_CONST_NONE 63 RETURN_VALUE children: ['Wallet']
simple_name: Walletraw bytecode: 46 08:14:02:28:68:20:84:07:84:07:84:0b:11:0f:16:10:10:02:16:11:80:2a:01:53:33:00:16:09:32:01:16:05:32:02:16:06:32:03:16:07:32:04:16:08:51:63prelude: (2, 0, 0, 0, 0, 0)args: []line info: 28:68:20:84:07:84:07:84:0b11:0f LOAD_NAME __name__16:10 STORE_NAME __module__10:02 LOAD_CONST_STRING Wallet16:11 STORE_NAME __qualname__80 LOAD_CONST_SMALL_INT 0 2a:01 BUILD_TUPLE 153 LOAD_NULL 33:00 MAKE_FUNCTION_DEFARGS 016:09 STORE_NAME __init__32:01 MAKE_FUNCTION 116:05 STORE_NAME deposit32:02 MAKE_FUNCTION 216:06 STORE_NAME withdraw32:03 MAKE_FUNCTION 316:07 STORE_NAME transfer32:04 MAKE_FUNCTION 416:08 STORE_NAME check_balance51 LOAD_CONST_NONE 63 RETURN_VALUE children: ['__init__', 'deposit', 'withdraw', 'transfer', 'check_balance']
simple_name: __init__raw bytecode: 19 a3:01:0c:09:12:0a:0b:40:24:b1:b0:18:0a:b2:b0:18:0b:51:63prelude: (5, 0, 0, 3, 0, 1)args: ['self', 'owner_name', 'balance']line info: 40:24b1 LOAD_FAST 1 b0 LOAD_FAST 0 18:0a STORE_ATTR owner_nameb2 LOAD_FAST 2 b0 LOAD_FAST 0 18:0b STORE_ATTR balance51 LOAD_CONST_NONE 63 RETURN_VALUE children: []
simple_name: depositraw bytecode: 50 32:10:05:12:13:60:60:25:29:51:b1:80:d8:44:5a:b0:57:13:0b:b1:e5:5a:18:0b:12:14:23:00:14:0c:b1:b0:13:0b:36:02:34:01:59:42:47:12:14:23:01:34:01:59:51:63prelude: (7, 0, 0, 2, 0, 0)args: ['self', 'amount']line info: 60:60:25:29:51b1 LOAD_FAST 1 80 LOAD_CONST_SMALL_INT 0 d8 BINARY_OP 1 __gt__ 44:5a POP_JUMP_IF_FALSE 26b0 LOAD_FAST 0 57 DUP_TOP 13:0b LOAD_ATTR balanceb1 LOAD_FAST 1 e5 BINARY_OP 14 __iadd__ 5a ROT_TWO 18:0b STORE_ATTR balance12:14 LOAD_GLOBAL print23:00 LOAD_CONST_OBJ 'Deposited ${}. New balance: ${}'14:0c LOAD_METHOD formatb1 LOAD_FAST 1 b0 LOAD_FAST 0 13:0b LOAD_ATTR balance36:02 CALL_METHOD 234:01 CALL_FUNCTION 159 POP_TOP 42:47 JUMP 712:14 LOAD_GLOBAL print23:01 LOAD_CONST_OBJ 'Invalid deposit amount.'34:01 CALL_FUNCTION 159 POP_TOP 51 LOAD_CONST_NONE 63 RETURN_VALUE children: []
simple_name: withdrawraw bytecode: 57 32:10:06:12:13:80:0d:2c:29:51:b1:80:d8:44:61:b0:13:0b:b1:db:44:5a:b0:57:13:0b:b1:e6:5a:18:0b:12:14:23:02:14:0c:b1:b0:13:0b:36:02:34:01:59:42:47:12:14:23:03:34:01:59:51:63prelude: (7, 0, 0, 2, 0, 0)args: ['self', 'amount']line info: 80:0d:2c:29:51b1 LOAD_FAST 1 80 LOAD_CONST_SMALL_INT 0 d8 BINARY_OP 1 __gt__ 44:61 POP_JUMP_IF_FALSE 33b0 LOAD_FAST 0 13:0b LOAD_ATTR balanceb1 LOAD_FAST 1 db BINARY_OP 4 __ge__ 44:5a POP_JUMP_IF_FALSE 26b0 LOAD_FAST 0 57 DUP_TOP 13:0b LOAD_ATTR balanceb1 LOAD_FAST 1 e6 BINARY_OP 15 __isub__ 5a ROT_TWO 18:0b STORE_ATTR balance12:14 LOAD_GLOBAL print23:02 LOAD_CONST_OBJ 'Withdrew ${}. New balance: ${}'14:0c LOAD_METHOD formatb1 LOAD_FAST 1 b0 LOAD_FAST 0 13:0b LOAD_ATTR balance36:02 CALL_METHOD 234:01 CALL_FUNCTION 159 POP_TOP 42:47 JUMP 712:14 LOAD_GLOBAL print23:03 LOAD_CONST_OBJ 'Invalid or insufficient funds for withdrawal.'34:01 CALL_FUNCTION 159 POP_TOP 51 LOAD_CONST_NONE 63 RETURN_VALUE children: []
simple_name: transferraw bytecode: 81 3b:18:07:12:15:13:80:14:2d:25:27:27:51:49:b0:13:0b:b2:db:44:73:b1:b0:de:d3:44:6d:b2:80:d8:44:5f:b0:14:06:b2:36:01:59:b1:14:05:b2:36:01:59:12:14:23:04:14:0c:b2:b1:13:0a:36:02:34:01:59:42:47:12:14:23:05:34:01:59:42:47:12:14:23:06:34:01:59:51:63prelude: (8, 0, 0, 3, 0, 0)args: ['self', 'recipient_wallet', 'amount']line info: 80:14:2d:25:27:27:51:49b0 LOAD_FAST 0 13:0b LOAD_ATTR balanceb2 LOAD_FAST 2 db BINARY_OP 4 __ge__ 44:73 POP_JUMP_IF_FALSE 51b1 LOAD_FAST 1 b0 LOAD_FAST 0 de BINARY_OP 7 <is> d3 UNARY_OP 3 <not> 44:6d POP_JUMP_IF_FALSE 45b2 LOAD_FAST 2 80 LOAD_CONST_SMALL_INT 0 d8 BINARY_OP 1 __gt__ 44:5f POP_JUMP_IF_FALSE 31b0 LOAD_FAST 0 14:06 LOAD_METHOD withdrawb2 LOAD_FAST 2 36:01 CALL_METHOD 159 POP_TOP b1 LOAD_FAST 1 14:05 LOAD_METHOD depositb2 LOAD_FAST 2 36:01 CALL_METHOD 159 POP_TOP 12:14 LOAD_GLOBAL print23:04 LOAD_CONST_OBJ 'Transferred ${} to {}.'14:0c LOAD_METHOD formatb2 LOAD_FAST 2 b1 LOAD_FAST 1 13:0a LOAD_ATTR owner_name36:02 CALL_METHOD 234:01 CALL_FUNCTION 159 POP_TOP 42:47 JUMP 712:14 LOAD_GLOBAL print23:05 LOAD_CONST_OBJ 'Invalid transfer amount.'34:01 CALL_FUNCTION 159 POP_TOP 42:47 JUMP 712:14 LOAD_GLOBAL print23:06 LOAD_CONST_OBJ 'Insufficient funds or invalid recipient.'34:01 CALL_FUNCTION 159 POP_TOP 51 LOAD_CONST_NONE 63 RETURN_VALUE children: []
simple_name: check_balanceraw bytecode: 22 21:08:08:12:80:1f:12:14:23:07:14:0c:b0:13:0b:36:01:34:01:59:51:63prelude: (5, 0, 0, 1, 0, 0)args: ['self']line info: 80:1f12:14 LOAD_GLOBAL print23:07 LOAD_CONST_OBJ 'Current balance: ${}'14:0c LOAD_METHOD formatb0 LOAD_FAST 0 13:0b LOAD_ATTR balance36:01 CALL_METHOD 134:01 CALL_FUNCTION 159 POP_TOP 51 LOAD_CONST_NONE 63 RETURN_VALUE children: []
这篇关于MicroPython字节码文件——mpy文件解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!