1 | /* |
2 | * This file is part of the MicroPython project, http://micropython.org/ |
3 | * |
4 | * The MIT License (MIT) |
5 | * |
6 | * Copyright (c) 2013-2020 Damien P. George |
7 | * |
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
9 | * of this software and associated documentation files (the "Software"), to deal |
10 | * in the Software without restriction, including without limitation the rights |
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
12 | * copies of the Software, and to permit persons to whom the Software is |
13 | * furnished to do so, subject to the following conditions: |
14 | * |
15 | * The above copyright notice and this permission notice shall be included in |
16 | * all copies or substantial portions of the Software. |
17 | * |
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
24 | * THE SOFTWARE. |
25 | */ |
26 | |
27 | #include <stdint.h> |
28 | #include <stdio.h> |
29 | #include <string.h> |
30 | #include <assert.h> |
31 | |
32 | #include "py/reader.h" |
33 | #include "py/nativeglue.h" |
34 | #include "py/persistentcode.h" |
35 | #include "py/bc0.h" |
36 | #include "py/objstr.h" |
37 | #include "py/mpthread.h" |
38 | |
39 | #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE |
40 | |
41 | #include "py/smallint.h" |
42 | |
43 | #define QSTR_LAST_STATIC MP_QSTR_zip |
44 | |
45 | #if MICROPY_DYNAMIC_COMPILER |
46 | #define MPY_FEATURE_ARCH_DYNAMIC mp_dynamic_compiler.native_arch |
47 | #else |
48 | #define MPY_FEATURE_ARCH_DYNAMIC MPY_FEATURE_ARCH |
49 | #endif |
50 | |
51 | #if MICROPY_PERSISTENT_CODE_LOAD || (MICROPY_PERSISTENT_CODE_SAVE && !MICROPY_DYNAMIC_COMPILER) |
52 | // The bytecode will depend on the number of bits in a small-int, and |
53 | // this function computes that (could make it a fixed constant, but it |
54 | // would need to be defined in mpconfigport.h). |
55 | STATIC int mp_small_int_bits(void) { |
56 | mp_int_t i = MP_SMALL_INT_MAX; |
57 | int n = 1; |
58 | while (i != 0) { |
59 | i >>= 1; |
60 | ++n; |
61 | } |
62 | return n; |
63 | } |
64 | #endif |
65 | |
66 | #define QSTR_WINDOW_SIZE (32) |
67 | |
68 | typedef struct _qstr_window_t { |
69 | uint16_t idx; // indexes the head of the window |
70 | uint16_t window[QSTR_WINDOW_SIZE]; |
71 | } qstr_window_t; |
72 | |
73 | // Push a qstr to the head of the window, and the tail qstr is overwritten |
74 | STATIC void qstr_window_push(qstr_window_t *qw, qstr qst) { |
75 | qw->idx = (qw->idx + 1) % QSTR_WINDOW_SIZE; |
76 | qw->window[qw->idx] = qst; |
77 | } |
78 | |
79 | // Pull an existing qstr from within the window to the head of the window |
80 | STATIC qstr qstr_window_pull(qstr_window_t *qw, size_t idx) { |
81 | qstr qst = qw->window[idx]; |
82 | if (idx > qw->idx) { |
83 | memmove(&qw->window[idx], &qw->window[idx + 1], (QSTR_WINDOW_SIZE - idx - 1) * sizeof(uint16_t)); |
84 | qw->window[QSTR_WINDOW_SIZE - 1] = qw->window[0]; |
85 | idx = 0; |
86 | } |
87 | memmove(&qw->window[idx], &qw->window[idx + 1], (qw->idx - idx) * sizeof(uint16_t)); |
88 | qw->window[qw->idx] = qst; |
89 | return qst; |
90 | } |
91 | |
92 | #if MICROPY_PERSISTENT_CODE_LOAD |
93 | |
94 | // Access a qstr at the given index, relative to the head of the window (0=head) |
95 | STATIC qstr qstr_window_access(qstr_window_t *qw, size_t idx) { |
96 | return qstr_window_pull(qw, (qw->idx + QSTR_WINDOW_SIZE - idx) % QSTR_WINDOW_SIZE); |
97 | } |
98 | |
99 | #endif |
100 | |
101 | #if MICROPY_PERSISTENT_CODE_SAVE |
102 | |
103 | // Insert a qstr at the head of the window, either by pulling an existing one or pushing a new one |
104 | STATIC size_t qstr_window_insert(qstr_window_t *qw, qstr qst) { |
105 | for (size_t idx = 0; idx < QSTR_WINDOW_SIZE; ++idx) { |
106 | if (qw->window[idx] == qst) { |
107 | qstr_window_pull(qw, idx); |
108 | return (qw->idx + QSTR_WINDOW_SIZE - idx) % QSTR_WINDOW_SIZE; |
109 | } |
110 | } |
111 | qstr_window_push(qw, qst); |
112 | return QSTR_WINDOW_SIZE; |
113 | } |
114 | |
115 | #endif |
116 | |
117 | typedef struct _bytecode_prelude_t { |
118 | uint n_state; |
119 | uint n_exc_stack; |
120 | uint scope_flags; |
121 | uint n_pos_args; |
122 | uint n_kwonly_args; |
123 | uint n_def_pos_args; |
124 | uint code_info_size; |
125 | } bytecode_prelude_t; |
126 | |
127 | // ip will point to start of opcodes |
128 | // return value will point to simple_name, source_file qstrs |
129 | STATIC byte *(const byte **ip, bytecode_prelude_t *prelude) { |
130 | MP_BC_PRELUDE_SIG_DECODE(*ip); |
131 | prelude->n_state = n_state; |
132 | prelude->n_exc_stack = n_exc_stack; |
133 | prelude->scope_flags = scope_flags; |
134 | prelude->n_pos_args = n_pos_args; |
135 | prelude->n_kwonly_args = n_kwonly_args; |
136 | prelude->n_def_pos_args = n_def_pos_args; |
137 | MP_BC_PRELUDE_SIZE_DECODE(*ip); |
138 | byte *ip_info = (byte *)*ip; |
139 | *ip += n_info; |
140 | *ip += n_cell; |
141 | return ip_info; |
142 | } |
143 | |
144 | #endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE |
145 | |
146 | #if MICROPY_PERSISTENT_CODE_LOAD |
147 | |
148 | #include "py/parsenum.h" |
149 | |
150 | STATIC int read_byte(mp_reader_t *reader); |
151 | STATIC size_t read_uint(mp_reader_t *reader, byte **out); |
152 | |
153 | #if MICROPY_EMIT_MACHINE_CODE |
154 | |
155 | typedef struct _reloc_info_t { |
156 | mp_reader_t *reader; |
157 | mp_uint_t *const_table; |
158 | } reloc_info_t; |
159 | |
160 | #if MICROPY_EMIT_THUMB |
161 | STATIC void asm_thumb_rewrite_mov(uint8_t *pc, uint16_t val) { |
162 | // high part |
163 | *(uint16_t *)pc = (*(uint16_t *)pc & 0xfbf0) | (val >> 1 & 0x0400) | (val >> 12); |
164 | // low part |
165 | *(uint16_t *)(pc + 2) = (*(uint16_t *)(pc + 2) & 0x0f00) | (val << 4 & 0x7000) | (val & 0x00ff); |
166 | |
167 | } |
168 | #endif |
169 | |
170 | STATIC void arch_link_qstr(uint8_t *pc, bool is_obj, qstr qst) { |
171 | mp_uint_t val = qst; |
172 | if (is_obj) { |
173 | val = (mp_uint_t)MP_OBJ_NEW_QSTR(qst); |
174 | } |
175 | #if MICROPY_EMIT_X86 || MICROPY_EMIT_X64 || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA || MICROPY_EMIT_XTENSAWIN |
176 | pc[0] = val & 0xff; |
177 | pc[1] = (val >> 8) & 0xff; |
178 | pc[2] = (val >> 16) & 0xff; |
179 | pc[3] = (val >> 24) & 0xff; |
180 | #elif MICROPY_EMIT_THUMB |
181 | if (is_obj) { |
182 | // qstr object, movw and movt |
183 | asm_thumb_rewrite_mov(pc, val); // movw |
184 | asm_thumb_rewrite_mov(pc + 4, val >> 16); // movt |
185 | } else { |
186 | // qstr number, movw instruction |
187 | asm_thumb_rewrite_mov(pc, val); // movw |
188 | } |
189 | #endif |
190 | } |
191 | |
192 | void mp_native_relocate(void *ri_in, uint8_t *text, uintptr_t reloc_text) { |
193 | // Relocate native code |
194 | reloc_info_t *ri = ri_in; |
195 | uint8_t op; |
196 | uintptr_t *addr_to_adjust = NULL; |
197 | while ((op = read_byte(ri->reader)) != 0xff) { |
198 | if (op & 1) { |
199 | // Point to new location to make adjustments |
200 | size_t addr = read_uint(ri->reader, NULL); |
201 | if ((addr & 1) == 0) { |
202 | // Point to somewhere in text |
203 | addr_to_adjust = &((uintptr_t *)text)[addr >> 1]; |
204 | } else { |
205 | // Point to somewhere in rodata |
206 | addr_to_adjust = &((uintptr_t *)ri->const_table[1])[addr >> 1]; |
207 | } |
208 | } |
209 | op >>= 1; |
210 | uintptr_t dest; |
211 | size_t n = 1; |
212 | if (op <= 5) { |
213 | if (op & 1) { |
214 | // Read in number of adjustments to make |
215 | n = read_uint(ri->reader, NULL); |
216 | } |
217 | op >>= 1; |
218 | if (op == 0) { |
219 | // Destination is text |
220 | dest = reloc_text; |
221 | } else { |
222 | // Destination is rodata (op=1) or bss (op=1 if no rodata, else op=2) |
223 | dest = ri->const_table[op]; |
224 | } |
225 | } else if (op == 6) { |
226 | // Destination is mp_fun_table itself |
227 | dest = (uintptr_t)&mp_fun_table; |
228 | } else { |
229 | // Destination is an entry in mp_fun_table |
230 | dest = ((uintptr_t *)&mp_fun_table)[op - 7]; |
231 | } |
232 | while (n--) { |
233 | *addr_to_adjust++ += dest; |
234 | } |
235 | } |
236 | } |
237 | |
238 | #endif |
239 | |
240 | STATIC int read_byte(mp_reader_t *reader) { |
241 | return reader->readbyte(reader->data); |
242 | } |
243 | |
244 | STATIC void read_bytes(mp_reader_t *reader, byte *buf, size_t len) { |
245 | while (len-- > 0) { |
246 | *buf++ = reader->readbyte(reader->data); |
247 | } |
248 | } |
249 | |
250 | STATIC size_t read_uint(mp_reader_t *reader, byte **out) { |
251 | size_t unum = 0; |
252 | for (;;) { |
253 | byte b = reader->readbyte(reader->data); |
254 | if (out != NULL) { |
255 | **out = b; |
256 | ++*out; |
257 | } |
258 | unum = (unum << 7) | (b & 0x7f); |
259 | if ((b & 0x80) == 0) { |
260 | break; |
261 | } |
262 | } |
263 | return unum; |
264 | } |
265 | |
266 | STATIC qstr load_qstr(mp_reader_t *reader, qstr_window_t *qw) { |
267 | size_t len = read_uint(reader, NULL); |
268 | if (len == 0) { |
269 | // static qstr |
270 | return read_byte(reader); |
271 | } |
272 | if (len & 1) { |
273 | // qstr in window |
274 | return qstr_window_access(qw, len >> 1); |
275 | } |
276 | len >>= 1; |
277 | char *str = m_new(char, len); |
278 | read_bytes(reader, (byte *)str, len); |
279 | qstr qst = qstr_from_strn(str, len); |
280 | m_del(char, str, len); |
281 | qstr_window_push(qw, qst); |
282 | return qst; |
283 | } |
284 | |
285 | STATIC mp_obj_t load_obj(mp_reader_t *reader) { |
286 | byte obj_type = read_byte(reader); |
287 | if (obj_type == 'e') { |
288 | return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj); |
289 | } else { |
290 | size_t len = read_uint(reader, NULL); |
291 | vstr_t vstr; |
292 | vstr_init_len(&vstr, len); |
293 | read_bytes(reader, (byte *)vstr.buf, len); |
294 | if (obj_type == 's' || obj_type == 'b') { |
295 | return mp_obj_new_str_from_vstr(obj_type == 's' ? &mp_type_str : &mp_type_bytes, &vstr); |
296 | } else if (obj_type == 'i') { |
297 | return mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL); |
298 | } else { |
299 | assert(obj_type == 'f' || obj_type == 'c'); |
300 | return mp_parse_num_decimal(vstr.buf, vstr.len, obj_type == 'c', false, NULL); |
301 | } |
302 | } |
303 | } |
304 | |
305 | STATIC void load_prelude_qstrs(mp_reader_t *reader, qstr_window_t *qw, byte *ip) { |
306 | qstr simple_name = load_qstr(reader, qw); |
307 | ip[0] = simple_name; |
308 | ip[1] = simple_name >> 8; |
309 | qstr source_file = load_qstr(reader, qw); |
310 | ip[2] = source_file; |
311 | ip[3] = source_file >> 8; |
312 | } |
313 | |
314 | STATIC void load_prelude(mp_reader_t *reader, qstr_window_t *qw, byte **ip, bytecode_prelude_t *prelude) { |
315 | // Read in the prelude header |
316 | byte *ip_read = *ip; |
317 | read_uint(reader, &ip_read); // read in n_state/etc (is effectively a var-uint) |
318 | read_uint(reader, &ip_read); // read in n_info/n_cell (is effectively a var-uint) |
319 | |
320 | // Prelude header has been read into *ip, now decode and extract values from it |
321 | extract_prelude((const byte **)ip, prelude); |
322 | |
323 | // Load qstrs in prelude |
324 | load_prelude_qstrs(reader, qw, ip_read); |
325 | ip_read += 4; |
326 | |
327 | // Read remaining code info |
328 | read_bytes(reader, ip_read, *ip - ip_read); |
329 | } |
330 | |
331 | STATIC void load_bytecode(mp_reader_t *reader, qstr_window_t *qw, byte *ip, byte *ip_top) { |
332 | while (ip < ip_top) { |
333 | *ip = read_byte(reader); |
334 | size_t sz; |
335 | uint f = mp_opcode_format(ip, &sz, false); |
336 | ++ip; |
337 | --sz; |
338 | if (f == MP_BC_FORMAT_QSTR) { |
339 | qstr qst = load_qstr(reader, qw); |
340 | *ip++ = qst; |
341 | *ip++ = qst >> 8; |
342 | sz -= 2; |
343 | } else if (f == MP_BC_FORMAT_VAR_UINT) { |
344 | while ((*ip++ = read_byte(reader)) & 0x80) { |
345 | } |
346 | } |
347 | read_bytes(reader, ip, sz); |
348 | ip += sz; |
349 | } |
350 | } |
351 | |
352 | STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) { |
353 | // Load function kind and data length |
354 | size_t kind_len = read_uint(reader, NULL); |
355 | int kind = (kind_len & 3) + MP_CODE_BYTECODE; |
356 | size_t fun_data_len = kind_len >> 2; |
357 | |
358 | #if !MICROPY_EMIT_MACHINE_CODE |
359 | if (kind != MP_CODE_BYTECODE) { |
360 | mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file" )); |
361 | } |
362 | #endif |
363 | |
364 | uint8_t *fun_data = NULL; |
365 | bytecode_prelude_t prelude = {0}; |
366 | #if MICROPY_EMIT_MACHINE_CODE |
367 | size_t prelude_offset = 0; |
368 | mp_uint_t type_sig = 0; |
369 | size_t n_qstr_link = 0; |
370 | #endif |
371 | |
372 | if (kind == MP_CODE_BYTECODE) { |
373 | // Allocate memory for the bytecode |
374 | fun_data = m_new(uint8_t, fun_data_len); |
375 | |
376 | // Load prelude |
377 | byte *ip = fun_data; |
378 | load_prelude(reader, qw, &ip, &prelude); |
379 | |
380 | // Load bytecode |
381 | load_bytecode(reader, qw, ip, fun_data + fun_data_len); |
382 | |
383 | #if MICROPY_EMIT_MACHINE_CODE |
384 | } else { |
385 | // Allocate memory for native data and load it |
386 | size_t fun_alloc; |
387 | MP_PLAT_ALLOC_EXEC(fun_data_len, (void **)&fun_data, &fun_alloc); |
388 | read_bytes(reader, fun_data, fun_data_len); |
389 | |
390 | if (kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER) { |
391 | // Parse qstr link table and link native code |
392 | n_qstr_link = read_uint(reader, NULL); |
393 | for (size_t i = 0; i < n_qstr_link; ++i) { |
394 | size_t off = read_uint(reader, NULL); |
395 | qstr qst = load_qstr(reader, qw); |
396 | uint8_t *dest = fun_data + (off >> 2); |
397 | if ((off & 3) == 0) { |
398 | // Generic 16-bit link |
399 | dest[0] = qst & 0xff; |
400 | dest[1] = (qst >> 8) & 0xff; |
401 | } else if ((off & 3) == 3) { |
402 | // Generic, aligned qstr-object link |
403 | *(mp_obj_t *)dest = MP_OBJ_NEW_QSTR(qst); |
404 | } else { |
405 | // Architecture-specific link |
406 | arch_link_qstr(dest, (off & 3) == 2, qst); |
407 | } |
408 | } |
409 | } |
410 | |
411 | if (kind == MP_CODE_NATIVE_PY) { |
412 | // Extract prelude for later use |
413 | prelude_offset = read_uint(reader, NULL); |
414 | const byte *ip = fun_data + prelude_offset; |
415 | byte *ip_info = extract_prelude(&ip, &prelude); |
416 | // Load qstrs in prelude |
417 | load_prelude_qstrs(reader, qw, ip_info); |
418 | } else { |
419 | // Load basic scope info for viper and asm |
420 | prelude.scope_flags = read_uint(reader, NULL); |
421 | prelude.n_pos_args = 0; |
422 | prelude.n_kwonly_args = 0; |
423 | if (kind == MP_CODE_NATIVE_ASM) { |
424 | prelude.n_pos_args = read_uint(reader, NULL); |
425 | type_sig = read_uint(reader, NULL); |
426 | } |
427 | } |
428 | #endif |
429 | } |
430 | |
431 | size_t n_obj = 0; |
432 | size_t n_raw_code = 0; |
433 | mp_uint_t *const_table = NULL; |
434 | |
435 | if (kind != MP_CODE_NATIVE_ASM) { |
436 | // Load constant table for bytecode, native and viper |
437 | |
438 | // Number of entries in constant table |
439 | n_obj = read_uint(reader, NULL); |
440 | n_raw_code = read_uint(reader, NULL); |
441 | |
442 | // Allocate constant table |
443 | size_t n_alloc = prelude.n_pos_args + prelude.n_kwonly_args + n_obj + n_raw_code; |
444 | #if MICROPY_EMIT_MACHINE_CODE |
445 | if (kind != MP_CODE_BYTECODE) { |
446 | ++n_alloc; // additional entry for mp_fun_table |
447 | if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRODATA) { |
448 | ++n_alloc; // additional entry for rodata |
449 | } |
450 | if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERBSS) { |
451 | ++n_alloc; // additional entry for BSS |
452 | } |
453 | } |
454 | #endif |
455 | |
456 | const_table = m_new(mp_uint_t, n_alloc); |
457 | mp_uint_t *ct = const_table; |
458 | |
459 | // Load function argument names (initial entries in const_table) |
460 | // (viper has n_pos_args=n_kwonly_args=0 so doesn't load any qstrs here) |
461 | for (size_t i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) { |
462 | *ct++ = (mp_uint_t)MP_OBJ_NEW_QSTR(load_qstr(reader, qw)); |
463 | } |
464 | |
465 | #if MICROPY_EMIT_MACHINE_CODE |
466 | if (kind != MP_CODE_BYTECODE) { |
467 | // Populate mp_fun_table entry |
468 | *ct++ = (mp_uint_t)(uintptr_t)&mp_fun_table; |
469 | |
470 | // Allocate and load rodata if needed |
471 | if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRODATA) { |
472 | size_t size = read_uint(reader, NULL); |
473 | uint8_t *rodata = m_new(uint8_t, size); |
474 | read_bytes(reader, rodata, size); |
475 | *ct++ = (uintptr_t)rodata; |
476 | } |
477 | |
478 | // Allocate BSS if needed |
479 | if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERBSS) { |
480 | size_t size = read_uint(reader, NULL); |
481 | uint8_t *bss = m_new0(uint8_t, size); |
482 | *ct++ = (uintptr_t)bss; |
483 | } |
484 | } |
485 | #endif |
486 | |
487 | // Load constant objects and raw code children |
488 | for (size_t i = 0; i < n_obj; ++i) { |
489 | *ct++ = (mp_uint_t)load_obj(reader); |
490 | } |
491 | for (size_t i = 0; i < n_raw_code; ++i) { |
492 | *ct++ = (mp_uint_t)(uintptr_t)load_raw_code(reader, qw); |
493 | } |
494 | } |
495 | |
496 | // Create raw_code and return it |
497 | mp_raw_code_t *rc = mp_emit_glue_new_raw_code(); |
498 | if (kind == MP_CODE_BYTECODE) { |
499 | // Assign bytecode to raw code object |
500 | mp_emit_glue_assign_bytecode(rc, fun_data, |
501 | #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS |
502 | fun_data_len, |
503 | #endif |
504 | const_table, |
505 | #if MICROPY_PERSISTENT_CODE_SAVE |
506 | n_obj, n_raw_code, |
507 | #endif |
508 | prelude.scope_flags); |
509 | |
510 | #if MICROPY_EMIT_MACHINE_CODE |
511 | } else { |
512 | // Relocate and commit code to executable address space |
513 | reloc_info_t ri = {reader, const_table}; |
514 | #if defined(MP_PLAT_COMMIT_EXEC) |
515 | void *opt_ri = (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRELOC) ? &ri : NULL; |
516 | fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len, opt_ri); |
517 | #else |
518 | if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRELOC) { |
519 | #if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE |
520 | // If native code needs relocations then it's not guaranteed that a pointer to |
521 | // the head of `buf` (containing the machine code) will be retained for the GC |
522 | // to trace. This is because native functions can start inside `buf` and so |
523 | // it's possible that the only GC-reachable pointers are pointers inside `buf`. |
524 | // So put this `buf` on a list of reachable root pointers. |
525 | if (MP_STATE_PORT(track_reloc_code_list) == MP_OBJ_NULL) { |
526 | MP_STATE_PORT(track_reloc_code_list) = mp_obj_new_list(0, NULL); |
527 | } |
528 | mp_obj_list_append(MP_STATE_PORT(track_reloc_code_list), MP_OBJ_FROM_PTR(fun_data)); |
529 | #endif |
530 | // Do the relocations. |
531 | mp_native_relocate(&ri, fun_data, (uintptr_t)fun_data); |
532 | } |
533 | #endif |
534 | |
535 | // Assign native code to raw code object |
536 | mp_emit_glue_assign_native(rc, kind, |
537 | fun_data, fun_data_len, const_table, |
538 | #if MICROPY_PERSISTENT_CODE_SAVE |
539 | prelude_offset, |
540 | n_obj, n_raw_code, |
541 | n_qstr_link, NULL, |
542 | #endif |
543 | prelude.n_pos_args, prelude.scope_flags, type_sig); |
544 | #endif |
545 | } |
546 | return rc; |
547 | } |
548 | |
549 | mp_raw_code_t *mp_raw_code_load(mp_reader_t *reader) { |
550 | byte header[4]; |
551 | read_bytes(reader, header, sizeof(header)); |
552 | if (header[0] != 'M' |
553 | || header[1] != MPY_VERSION |
554 | || MPY_FEATURE_DECODE_FLAGS(header[2]) != MPY_FEATURE_FLAGS |
555 | || header[3] > mp_small_int_bits() |
556 | || read_uint(reader, NULL) > QSTR_WINDOW_SIZE) { |
557 | mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file" )); |
558 | } |
559 | if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) { |
560 | byte arch = MPY_FEATURE_DECODE_ARCH(header[2]); |
561 | if (!MPY_FEATURE_ARCH_TEST(arch)) { |
562 | mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy arch" )); |
563 | } |
564 | } |
565 | qstr_window_t qw; |
566 | qw.idx = 0; |
567 | mp_raw_code_t *rc = load_raw_code(reader, &qw); |
568 | reader->close(reader->data); |
569 | return rc; |
570 | } |
571 | |
572 | mp_raw_code_t *mp_raw_code_load_mem(const byte *buf, size_t len) { |
573 | mp_reader_t reader; |
574 | mp_reader_new_mem(&reader, buf, len, 0); |
575 | return mp_raw_code_load(&reader); |
576 | } |
577 | |
578 | #if MICROPY_HAS_FILE_READER |
579 | |
580 | mp_raw_code_t *mp_raw_code_load_file(const char *filename) { |
581 | mp_reader_t reader; |
582 | mp_reader_new_file(&reader, filename); |
583 | return mp_raw_code_load(&reader); |
584 | } |
585 | |
586 | #endif // MICROPY_HAS_FILE_READER |
587 | |
588 | #endif // MICROPY_PERSISTENT_CODE_LOAD |
589 | |
590 | #if MICROPY_PERSISTENT_CODE_SAVE |
591 | |
592 | #include "py/objstr.h" |
593 | |
594 | STATIC void mp_print_bytes(mp_print_t *print, const byte *data, size_t len) { |
595 | print->print_strn(print->data, (const char *)data, len); |
596 | } |
597 | |
598 | #define BYTES_FOR_INT ((MP_BYTES_PER_OBJ_WORD * 8 + 6) / 7) |
599 | STATIC void mp_print_uint(mp_print_t *print, size_t n) { |
600 | byte buf[BYTES_FOR_INT]; |
601 | byte *p = buf + sizeof(buf); |
602 | *--p = n & 0x7f; |
603 | n >>= 7; |
604 | for (; n != 0; n >>= 7) { |
605 | *--p = 0x80 | (n & 0x7f); |
606 | } |
607 | print->print_strn(print->data, (char *)p, buf + sizeof(buf) - p); |
608 | } |
609 | |
610 | STATIC void save_qstr(mp_print_t *print, qstr_window_t *qw, qstr qst) { |
611 | if (qst <= QSTR_LAST_STATIC) { |
612 | // encode static qstr |
613 | byte buf[2] = {0, qst & 0xff}; |
614 | mp_print_bytes(print, buf, 2); |
615 | return; |
616 | } |
617 | size_t idx = qstr_window_insert(qw, qst); |
618 | if (idx < QSTR_WINDOW_SIZE) { |
619 | // qstr found in window, encode index to it |
620 | mp_print_uint(print, idx << 1 | 1); |
621 | return; |
622 | } |
623 | size_t len; |
624 | const byte *str = qstr_data(qst, &len); |
625 | mp_print_uint(print, len << 1); |
626 | mp_print_bytes(print, str, len); |
627 | } |
628 | |
629 | STATIC void save_obj(mp_print_t *print, mp_obj_t o) { |
630 | if (mp_obj_is_str_or_bytes(o)) { |
631 | byte obj_type; |
632 | if (mp_obj_is_str(o)) { |
633 | obj_type = 's'; |
634 | } else { |
635 | obj_type = 'b'; |
636 | } |
637 | size_t len; |
638 | const char *str = mp_obj_str_get_data(o, &len); |
639 | mp_print_bytes(print, &obj_type, 1); |
640 | mp_print_uint(print, len); |
641 | mp_print_bytes(print, (const byte *)str, len); |
642 | } else if (MP_OBJ_TO_PTR(o) == &mp_const_ellipsis_obj) { |
643 | byte obj_type = 'e'; |
644 | mp_print_bytes(print, &obj_type, 1); |
645 | } else { |
646 | // we save numbers using a simplistic text representation |
647 | // TODO could be improved |
648 | byte obj_type; |
649 | if (mp_obj_is_type(o, &mp_type_int)) { |
650 | obj_type = 'i'; |
651 | #if MICROPY_PY_BUILTINS_COMPLEX |
652 | } else if (mp_obj_is_type(o, &mp_type_complex)) { |
653 | obj_type = 'c'; |
654 | #endif |
655 | } else { |
656 | assert(mp_obj_is_float(o)); |
657 | obj_type = 'f'; |
658 | } |
659 | vstr_t vstr; |
660 | mp_print_t pr; |
661 | vstr_init_print(&vstr, 10, &pr); |
662 | mp_obj_print_helper(&pr, o, PRINT_REPR); |
663 | mp_print_bytes(print, &obj_type, 1); |
664 | mp_print_uint(print, vstr.len); |
665 | mp_print_bytes(print, (const byte *)vstr.buf, vstr.len); |
666 | vstr_clear(&vstr); |
667 | } |
668 | } |
669 | |
670 | STATIC void save_prelude_qstrs(mp_print_t *print, qstr_window_t *qw, const byte *ip) { |
671 | save_qstr(print, qw, ip[0] | (ip[1] << 8)); // simple_name |
672 | save_qstr(print, qw, ip[2] | (ip[3] << 8)); // source_file |
673 | } |
674 | |
675 | STATIC void save_bytecode(mp_print_t *print, qstr_window_t *qw, const byte *ip, const byte *ip_top) { |
676 | while (ip < ip_top) { |
677 | size_t sz; |
678 | uint f = mp_opcode_format(ip, &sz, true); |
679 | if (f == MP_BC_FORMAT_QSTR) { |
680 | mp_print_bytes(print, ip, 1); |
681 | qstr qst = ip[1] | (ip[2] << 8); |
682 | save_qstr(print, qw, qst); |
683 | ip += 3; |
684 | sz -= 3; |
685 | } |
686 | mp_print_bytes(print, ip, sz); |
687 | ip += sz; |
688 | } |
689 | } |
690 | |
691 | STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc, qstr_window_t *qstr_window) { |
692 | // Save function kind and data length |
693 | mp_print_uint(print, (rc->fun_data_len << 2) | (rc->kind - MP_CODE_BYTECODE)); |
694 | |
695 | bytecode_prelude_t prelude; |
696 | |
697 | if (rc->kind == MP_CODE_BYTECODE) { |
698 | // Extract prelude |
699 | const byte *ip = rc->fun_data; |
700 | const byte *ip_info = extract_prelude(&ip, &prelude); |
701 | |
702 | // Save prelude |
703 | mp_print_bytes(print, rc->fun_data, ip_info - (const byte *)rc->fun_data); |
704 | save_prelude_qstrs(print, qstr_window, ip_info); |
705 | ip_info += 4; |
706 | mp_print_bytes(print, ip_info, ip - ip_info); |
707 | |
708 | // Save bytecode |
709 | const byte *ip_top = (const byte *)rc->fun_data + rc->fun_data_len; |
710 | save_bytecode(print, qstr_window, ip, ip_top); |
711 | #if MICROPY_EMIT_MACHINE_CODE |
712 | } else { |
713 | // Save native code |
714 | mp_print_bytes(print, rc->fun_data, rc->fun_data_len); |
715 | |
716 | if (rc->kind == MP_CODE_NATIVE_PY || rc->kind == MP_CODE_NATIVE_VIPER) { |
717 | // Save qstr link table for native code |
718 | mp_print_uint(print, rc->n_qstr); |
719 | for (size_t i = 0; i < rc->n_qstr; ++i) { |
720 | mp_print_uint(print, rc->qstr_link[i].off); |
721 | save_qstr(print, qstr_window, rc->qstr_link[i].qst); |
722 | } |
723 | } |
724 | |
725 | if (rc->kind == MP_CODE_NATIVE_PY) { |
726 | // Save prelude size |
727 | mp_print_uint(print, rc->prelude_offset); |
728 | |
729 | // Extract prelude and save qstrs in prelude |
730 | const byte *ip = (const byte *)rc->fun_data + rc->prelude_offset; |
731 | const byte *ip_info = extract_prelude(&ip, &prelude); |
732 | save_prelude_qstrs(print, qstr_window, ip_info); |
733 | } else { |
734 | // Save basic scope info for viper and asm |
735 | mp_print_uint(print, rc->scope_flags & MP_SCOPE_FLAG_ALL_SIG); |
736 | prelude.n_pos_args = 0; |
737 | prelude.n_kwonly_args = 0; |
738 | if (rc->kind == MP_CODE_NATIVE_ASM) { |
739 | mp_print_uint(print, rc->n_pos_args); |
740 | mp_print_uint(print, rc->type_sig); |
741 | } |
742 | } |
743 | #endif |
744 | } |
745 | |
746 | if (rc->kind != MP_CODE_NATIVE_ASM) { |
747 | // Save constant table for bytecode, native and viper |
748 | |
749 | // Number of entries in constant table |
750 | mp_print_uint(print, rc->n_obj); |
751 | mp_print_uint(print, rc->n_raw_code); |
752 | |
753 | const mp_uint_t *const_table = rc->const_table; |
754 | |
755 | // Save function argument names (initial entries in const_table) |
756 | // (viper has n_pos_args=n_kwonly_args=0 so doesn't save any qstrs here) |
757 | for (size_t i = 0; i < prelude.n_pos_args + prelude.n_kwonly_args; ++i) { |
758 | mp_obj_t o = (mp_obj_t)*const_table++; |
759 | save_qstr(print, qstr_window, MP_OBJ_QSTR_VALUE(o)); |
760 | } |
761 | |
762 | if (rc->kind != MP_CODE_BYTECODE) { |
763 | // Skip saving mp_fun_table entry |
764 | ++const_table; |
765 | } |
766 | |
767 | // Save constant objects and raw code children |
768 | for (size_t i = 0; i < rc->n_obj; ++i) { |
769 | save_obj(print, (mp_obj_t)*const_table++); |
770 | } |
771 | for (size_t i = 0; i < rc->n_raw_code; ++i) { |
772 | save_raw_code(print, (mp_raw_code_t *)(uintptr_t)*const_table++, qstr_window); |
773 | } |
774 | } |
775 | } |
776 | |
777 | STATIC bool mp_raw_code_has_native(mp_raw_code_t *rc) { |
778 | if (rc->kind != MP_CODE_BYTECODE) { |
779 | return true; |
780 | } |
781 | |
782 | const byte *ip = rc->fun_data; |
783 | bytecode_prelude_t prelude; |
784 | extract_prelude(&ip, &prelude); |
785 | |
786 | const mp_uint_t *const_table = rc->const_table |
787 | + prelude.n_pos_args + prelude.n_kwonly_args |
788 | + rc->n_obj; |
789 | |
790 | for (size_t i = 0; i < rc->n_raw_code; ++i) { |
791 | if (mp_raw_code_has_native((mp_raw_code_t *)(uintptr_t)*const_table++)) { |
792 | return true; |
793 | } |
794 | } |
795 | |
796 | return false; |
797 | } |
798 | |
799 | void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print) { |
800 | // header contains: |
801 | // byte 'M' |
802 | // byte version |
803 | // byte feature flags |
804 | // byte number of bits in a small int |
805 | // uint size of qstr window |
806 | byte [4] = { |
807 | 'M', |
808 | MPY_VERSION, |
809 | MPY_FEATURE_ENCODE_FLAGS(MPY_FEATURE_FLAGS_DYNAMIC), |
810 | #if MICROPY_DYNAMIC_COMPILER |
811 | mp_dynamic_compiler.small_int_bits, |
812 | #else |
813 | mp_small_int_bits(), |
814 | #endif |
815 | }; |
816 | if (mp_raw_code_has_native(rc)) { |
817 | header[2] |= MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC); |
818 | } |
819 | mp_print_bytes(print, header, sizeof(header)); |
820 | mp_print_uint(print, QSTR_WINDOW_SIZE); |
821 | |
822 | qstr_window_t qw; |
823 | qw.idx = 0; |
824 | memset(qw.window, 0, sizeof(qw.window)); |
825 | save_raw_code(print, rc, &qw); |
826 | } |
827 | |
828 | #if MICROPY_PERSISTENT_CODE_SAVE_FILE |
829 | |
830 | #include <unistd.h> |
831 | #include <sys/stat.h> |
832 | #include <fcntl.h> |
833 | |
834 | STATIC void fd_print_strn(void *env, const char *str, size_t len) { |
835 | int fd = (intptr_t)env; |
836 | MP_THREAD_GIL_EXIT(); |
837 | ssize_t ret = write(fd, str, len); |
838 | MP_THREAD_GIL_ENTER(); |
839 | (void)ret; |
840 | } |
841 | |
842 | void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename) { |
843 | MP_THREAD_GIL_EXIT(); |
844 | int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); |
845 | MP_THREAD_GIL_ENTER(); |
846 | mp_print_t fd_print = {(void *)(intptr_t)fd, fd_print_strn}; |
847 | mp_raw_code_save(rc, &fd_print); |
848 | MP_THREAD_GIL_EXIT(); |
849 | close(fd); |
850 | MP_THREAD_GIL_ENTER(); |
851 | } |
852 | |
853 | #endif // MICROPY_PERSISTENT_CODE_SAVE_FILE |
854 | |
855 | #endif // MICROPY_PERSISTENT_CODE_SAVE |
856 | |