.pyc
i file contengono alcuni metadati e un oggetto marshal
edcode
; per caricare l'oggetto code
e smontare tale uso:
import dis, marshal, sys
# Header size changed in 3.3. It might change again, but as of this writing, it hasn't.
header_size = 12 if sys.version_info >= (3, 3) else 8
with open(pycfile, "rb") as f:
magic_and_timestamp = f.read(header_size) # first 8 or 12 bytes are metadata
code = marshal.load(f) # rest is a marshalled code object
dis.dis(code)
demo con il modulo bisect
:
>>> import bisect
>>> import dis, marshal
>>> import sys
>>> header_size = 12 if sys.version_info >= (3, 3) else 8
>>> with open(bisect.__file__, "rb") as f:
... magic_and_timestamp = f.read(header_size) # first 8 or 12 bytes are metadata
... code = marshal.load(f) # rest is bytecode
...
>>> dis.dis(code)
1 0 LOAD_CONST 0 ('Bisection algorithms.')
3 STORE_NAME 0 (__doc__)
3 6 LOAD_CONST 1 (0)
9 LOAD_CONST 8 (None)
12 LOAD_CONST 2 (<code object insort_right at 0x106a459b0, file "/Users/mpieters/Development/Library/buildout.python/parts/opt/lib/python2.7/bisect.py", line 3>)
15 MAKE_FUNCTION 2
18 STORE_NAME 2 (insort_right)
22 21 LOAD_NAME 2 (insort_right)
24 STORE_NAME 3 (insort)
24 27 LOAD_CONST 1 (0)
30 LOAD_CONST 8 (None)
33 LOAD_CONST 3 (<code object bisect_right at 0x106a45ab0, file "/Users/mpieters/Development/Library/buildout.python/parts/opt/lib/python2.7/bisect.py", line 24>)
36 MAKE_FUNCTION 2
39 STORE_NAME 4 (bisect_right)
45 42 LOAD_NAME 4 (bisect_right)
45 STORE_NAME 5 (bisect)
47 48 LOAD_CONST 1 (0)
51 LOAD_CONST 8 (None)
54 LOAD_CONST 4 (<code object insort_left at 0x106a45bb0, file "/Users/mpieters/Development/Library/buildout.python/parts/opt/lib/python2.7/bisect.py", line 47>)
57 MAKE_FUNCTION 2
60 STORE_NAME 6 (insort_left)
67 63 LOAD_CONST 1 (0)
66 LOAD_CONST 8 (None)
69 LOAD_CONST 5 (<code object bisect_left at 0x106a45cb0, file "/Users/mpieters/Development/Library/buildout.python/parts/opt/lib/python2.7/bisect.py", line 67>)
72 MAKE_FUNCTION 2
75 STORE_NAME 7 (bisect_left)
89 78 SETUP_EXCEPT 14 (to 95)
90 81 LOAD_CONST 6 (-1)
84 LOAD_CONST 7 (('*',))
87 IMPORT_NAME 8 (_bisect)
90 IMPORT_STAR
91 POP_BLOCK
92 JUMP_FORWARD 17 (to 112)
91 >> 95 DUP_TOP
96 LOAD_NAME 9 (ImportError)
99 COMPARE_OP 10 (exception match)
102 POP_JUMP_IF_FALSE 111
105 POP_TOP
106 POP_TOP
107 POP_TOP
92 108 JUMP_FORWARD 1 (to 112)
>> 111 END_FINALLY
>> 112 LOAD_CONST 8 (None)
115 RETURN_VALUE
noti che questo è solo il codice oggetto livello superiore, definendo il modulo. Se si desidera analizzare le funzioni contenute, è necessario caricare gli oggetti nidificati code
, dall'array code.co_consts
di livello superiore; per esempio, oggetto codice della funzione insort_right
è caricato con LOAD_CONST 2
, in modo da cercare il codice oggetto in tale indice:
>>> code.co_consts[2]
<code object insort_right at 0x106a459b0, file "/Users/mpieters/Development/Library/buildout.python/parts/opt/lib/python2.7/bisect.py", line 3>
>>> dis.dis(code.co_consts[2])
12 0 LOAD_FAST 2 (lo)
3 LOAD_CONST 1 (0)
6 COMPARE_OP 0 (<)
9 POP_JUMP_IF_FALSE 27
13 12 LOAD_GLOBAL 0 (ValueError)
15 LOAD_CONST 2 ('lo must be non-negative')
18 CALL_FUNCTION 1
21 RAISE_VARARGS 1
24 JUMP_FORWARD 0 (to 27)
14 >> 27 LOAD_FAST 3 (hi)
30 LOAD_CONST 5 (None)
33 COMPARE_OP 8 (is)
36 POP_JUMP_IF_FALSE 54
15 39 LOAD_GLOBAL 2 (len)
42 LOAD_FAST 0 (a)
45 CALL_FUNCTION 1
48 STORE_FAST 3 (hi)
51 JUMP_FORWARD 0 (to 54)
16 >> 54 SETUP_LOOP 65 (to 122)
>> 57 LOAD_FAST 2 (lo)
60 LOAD_FAST 3 (hi)
63 COMPARE_OP 0 (<)
66 POP_JUMP_IF_FALSE 121
17 69 LOAD_FAST 2 (lo)
72 LOAD_FAST 3 (hi)
75 BINARY_ADD
76 LOAD_CONST 3 (2)
79 BINARY_FLOOR_DIVIDE
80 STORE_FAST 4 (mid)
18 83 LOAD_FAST 1 (x)
86 LOAD_FAST 0 (a)
89 LOAD_FAST 4 (mid)
92 BINARY_SUBSCR
93 COMPARE_OP 0 (<)
96 POP_JUMP_IF_FALSE 108
99 LOAD_FAST 4 (mid)
102 STORE_FAST 3 (hi)
105 JUMP_ABSOLUTE 57
19 >> 108 LOAD_FAST 4 (mid)
111 LOAD_CONST 4 (1)
114 BINARY_ADD
115 STORE_FAST 2 (lo)
118 JUMP_ABSOLUTE 57
>> 121 POP_BLOCK
20 >> 122 LOAD_FAST 0 (a)
125 LOAD_ATTR 3 (insert)
128 LOAD_FAST 2 (lo)
131 LOAD_FAST 1 (x)
134 CALL_FUNCTION 2
137 POP_TOP
138 LOAD_CONST 5 (None)
141 RETURN_VALUE
Personalmente mi evitare cercando di analizzare il file .pyc
con qualcosa di diverso rispetto alla versione corrispondente di Python e il modulo marshal
. Il formato marshal
è fondamentalmente un formato di serializzazione interno che cambia con le esigenze di Python stesso. Le nuove funzionalità come le list comprehensions e le dichiarazioni with
e async
/await
richiedono nuove aggiunte al formato, che non viene pubblicato diverso da C source code.
Se si percorre questo percorso e si arriva a read a code
object con altri mezzi rispetto all'utilizzo del modulo, è necessario analizzare lo smontaggio dai vari attributi dell'oggetto codice; vedere dis
module source per i dettagli su come eseguire questa operazione (sarà necessario utilizzare gli attributi co_firstlineno
e co_lnotab
per creare una mappa bytecode-offset-to-linenumber, ad esempio).
il mio scopo è scrivere un programma in python che prenda un file .pyc e generi output come un modulo dis.dis() senza usare il modulo 'dis'. Così come posso capire l'inizio di un codice, numero di riga ecc. da un file .pyc? –
C'è un breve post sul blog ['formato file pyc'] (http://nedbatchelder.com/blog/200804/the_structure_of_pyc_files.html). Ma sono sicuro di capirli tutti completamente. Devi tuffarti nella fonte di Python (che ha molta buona documentazione in quest'area). – HelloWorld
@NiyaSimonC: vuoi dire che vuoi analizzare il file '.pyc' da zero? Ciò richiede l'analisi del formato 'marshal', per vedere [come codifica un oggetto' code'] (https://hg.python.org/cpython/file/2.7/Python/marshal.c#l423). Da lì, estrai le informazioni per gli oggetti codice (vedi [modello dati] (https://docs.python.org/2/reference/datamodel.html), scorri verso il basso fino a Tipi interni -> oggetti codice) e associa i byte a istruzioni bytecode (vedi la funzione ['dis.disassemble()'] (https://hg.python.org/cpython/file/2.7/Lib/dis.py#l61). –