Branch data Line data Source code
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 : : typedef struct _bytecode_prelude_t {
52 : : uint n_state;
53 : : uint n_exc_stack;
54 : : uint scope_flags;
55 : : uint n_pos_args;
56 : : uint n_kwonly_args;
57 : : uint n_def_pos_args;
58 : : uint code_info_size;
59 : : } bytecode_prelude_t;
60 : :
61 : : #endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
62 : :
63 : : #if MICROPY_PERSISTENT_CODE_LOAD
64 : :
65 : : #include "py/parsenum.h"
66 : :
67 : : STATIC int read_byte(mp_reader_t *reader);
68 : : STATIC size_t read_uint(mp_reader_t *reader);
69 : :
70 : : #if MICROPY_EMIT_MACHINE_CODE
71 : :
72 : : typedef struct _reloc_info_t {
73 : : mp_reader_t *reader;
74 : : mp_module_context_t *context;
75 : : uint8_t *rodata;
76 : : uint8_t *bss;
77 : : } reloc_info_t;
78 : :
79 : 8 : void mp_native_relocate(void *ri_in, uint8_t *text, uintptr_t reloc_text) {
80 : : // Relocate native code
81 : 8 : reloc_info_t *ri = ri_in;
82 : 8 : uint8_t op;
83 : 8 : uintptr_t *addr_to_adjust = NULL;
84 [ + + ]: 28 : while ((op = read_byte(ri->reader)) != 0xff) {
85 [ + - ]: 20 : if (op & 1) {
86 : : // Point to new location to make adjustments
87 : 20 : size_t addr = read_uint(ri->reader);
88 [ + + ]: 20 : if ((addr & 1) == 0) {
89 : : // Point to somewhere in text
90 : 16 : addr_to_adjust = &((uintptr_t *)text)[addr >> 1];
91 : : } else {
92 : : // Point to somewhere in rodata
93 : 4 : addr_to_adjust = &((uintptr_t *)ri->rodata)[addr >> 1];
94 : : }
95 : : }
96 : 20 : op >>= 1;
97 : 20 : uintptr_t dest;
98 : 20 : size_t n = 1;
99 [ + + ]: 20 : if (op <= 5) {
100 [ + + ]: 8 : if (op & 1) {
101 : : // Read in number of adjustments to make
102 : 4 : n = read_uint(ri->reader);
103 : : }
104 : 8 : op >>= 1;
105 [ - + ]: 8 : if (op == 0) {
106 : : // Destination is text
107 : 20 : dest = reloc_text;
108 [ # # ]: 0 : } else if (op == 1) {
109 : : // Destination is rodata
110 : 0 : dest = (uintptr_t)ri->rodata;
111 : : } else {
112 : : // Destination is bss
113 : 0 : dest = (uintptr_t)ri->bss;
114 : : }
115 [ + + ]: 12 : } else if (op == 6) {
116 : : // Destination is qstr_table
117 : 4 : dest = (uintptr_t)ri->context->constants.qstr_table;
118 [ - + ]: 8 : } else if (op == 7) {
119 : : // Destination is obj_table
120 : 0 : dest = (uintptr_t)ri->context->constants.obj_table;
121 [ + + ]: 8 : } else if (op == 8) {
122 : : // Destination is mp_fun_table itself
123 : 4 : dest = (uintptr_t)&mp_fun_table;
124 : : } else {
125 : : // Destination is an entry in mp_fun_table
126 : 4 : dest = ((uintptr_t *)&mp_fun_table)[op - 9];
127 : : }
128 [ + + ]: 36 : while (n--) {
129 : 16 : *addr_to_adjust++ += dest;
130 : : }
131 : : }
132 : 8 : }
133 : :
134 : : #endif
135 : :
136 : 21511 : STATIC int read_byte(mp_reader_t *reader) {
137 : 21511 : return reader->readbyte(reader->data);
138 : : }
139 : :
140 : 21167 : STATIC void read_bytes(mp_reader_t *reader, byte *buf, size_t len) {
141 [ + + ]: 1915309 : while (len-- > 0) {
142 : 1894142 : *buf++ = reader->readbyte(reader->data);
143 : : }
144 : 21167 : }
145 : :
146 : 33736 : STATIC size_t read_uint(mp_reader_t *reader) {
147 : 33736 : size_t unum = 0;
148 : 42434 : for (;;) {
149 : 42434 : byte b = reader->readbyte(reader->data);
150 : 42434 : unum = (unum << 7) | (b & 0x7f);
151 [ + + ]: 42434 : if ((b & 0x80) == 0) {
152 : : break;
153 : : }
154 : : }
155 : 33736 : return unum;
156 : : }
157 : :
158 : 17510 : STATIC qstr load_qstr(mp_reader_t *reader) {
159 : 17510 : size_t len = read_uint(reader);
160 [ + + ]: 17510 : if (len & 1) {
161 : : // static qstr
162 : 7620 : return len >> 1;
163 : : }
164 : 9890 : len >>= 1;
165 : 9890 : char *str = m_new(char, len);
166 : 9890 : read_bytes(reader, (byte *)str, len);
167 : 9890 : read_byte(reader); // read and discard null terminator
168 : 9890 : qstr qst = qstr_from_strn(str, len);
169 : 9890 : m_del(char, str, len);
170 : 9890 : return qst;
171 : : }
172 : :
173 : 7267 : STATIC mp_obj_t load_obj(mp_reader_t *reader) {
174 : 7267 : byte obj_type = read_byte(reader);
175 : : #if MICROPY_EMIT_MACHINE_CODE
176 [ + + + + : 7267 : if (obj_type == MP_PERSISTENT_OBJ_FUN_TABLE) {
+ + ]
177 : : return MP_OBJ_FROM_PTR(&mp_fun_table);
178 : : } else
179 : : #endif
180 : : if (obj_type == MP_PERSISTENT_OBJ_NONE) {
181 : 4 : return mp_const_none;
182 : : } else if (obj_type == MP_PERSISTENT_OBJ_FALSE) {
183 : 8 : return mp_const_false;
184 : : } else if (obj_type == MP_PERSISTENT_OBJ_TRUE) {
185 : 10 : return mp_const_true;
186 : : } else if (obj_type == MP_PERSISTENT_OBJ_ELLIPSIS) {
187 : 6 : return MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj);
188 : : } else {
189 : 6553 : size_t len = read_uint(reader);
190 [ + + ]: 6553 : if (len == 0 && obj_type == MP_PERSISTENT_OBJ_BYTES) {
191 : 40 : read_byte(reader); // skip null terminator
192 : 40 : return mp_const_empty_bytes;
193 [ + + ]: 6513 : } else if (obj_type == MP_PERSISTENT_OBJ_TUPLE) {
194 : 445 : mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(len, NULL));
195 [ + + ]: 1645 : for (size_t i = 0; i < len; ++i) {
196 : 1200 : tuple->items[i] = load_obj(reader);
197 : : }
198 : : return MP_OBJ_FROM_PTR(tuple);
199 : : }
200 : 6068 : vstr_t vstr;
201 : 6068 : vstr_init_len(&vstr, len);
202 : 6068 : read_bytes(reader, (byte *)vstr.buf, len);
203 [ + + ]: 6068 : if (obj_type == MP_PERSISTENT_OBJ_STR || obj_type == MP_PERSISTENT_OBJ_BYTES) {
204 : 4286 : read_byte(reader); // skip null terminator
205 [ + + ]: 4286 : if (obj_type == MP_PERSISTENT_OBJ_STR) {
206 : 3690 : return mp_obj_new_str_from_utf8_vstr(&vstr);
207 : : } else {
208 : 596 : return mp_obj_new_bytes_from_vstr(&vstr);
209 : : }
210 [ + + ]: 1782 : } else if (obj_type == MP_PERSISTENT_OBJ_INT) {
211 : 1230 : return mp_parse_num_integer(vstr.buf, vstr.len, 10, NULL);
212 : : } else {
213 [ - + ]: 552 : assert(obj_type == MP_PERSISTENT_OBJ_FLOAT || obj_type == MP_PERSISTENT_OBJ_COMPLEX);
214 : 552 : return mp_parse_num_float(vstr.buf, vstr.len, obj_type == MP_PERSISTENT_OBJ_COMPLEX, NULL);
215 : : }
216 : : }
217 : : }
218 : :
219 : 3864 : STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *context) {
220 : : // Load function kind and data length
221 : 3864 : size_t kind_len = read_uint(reader);
222 : 3864 : int kind = (kind_len & 3) + MP_CODE_BYTECODE;
223 : 3864 : bool has_children = !!(kind_len & 4);
224 : 3864 : size_t fun_data_len = kind_len >> 3;
225 : :
226 : : #if !MICROPY_EMIT_MACHINE_CODE
227 : : if (kind != MP_CODE_BYTECODE) {
228 : : mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file"));
229 : : }
230 : : #endif
231 : :
232 : 3864 : uint8_t *fun_data = NULL;
233 : : #if MICROPY_EMIT_MACHINE_CODE
234 : 3864 : size_t prelude_offset = 0;
235 : 3864 : mp_uint_t native_scope_flags = 0;
236 : 3864 : mp_uint_t native_n_pos_args = 0;
237 : 3864 : mp_uint_t native_type_sig = 0;
238 : : #endif
239 : :
240 [ + + ]: 3864 : if (kind == MP_CODE_BYTECODE) {
241 : : // Allocate memory for the bytecode
242 : 1854 : fun_data = m_new(uint8_t, fun_data_len);
243 : : // Load bytecode
244 : 1854 : read_bytes(reader, fun_data, fun_data_len);
245 : :
246 : : #if MICROPY_EMIT_MACHINE_CODE
247 : : } else {
248 : : // Allocate memory for native data and load it
249 : 2010 : size_t fun_alloc;
250 : 2010 : MP_PLAT_ALLOC_EXEC(fun_data_len, (void **)&fun_data, &fun_alloc);
251 : 2010 : read_bytes(reader, fun_data, fun_data_len);
252 : :
253 [ + + ]: 2010 : if (kind == MP_CODE_NATIVE_PY) {
254 : : // Read prelude offset within fun_data, and extract scope flags.
255 : 1794 : prelude_offset = read_uint(reader);
256 : 1794 : const byte *ip = fun_data + prelude_offset;
257 [ + + ]: 2086 : MP_BC_PRELUDE_SIG_DECODE(ip);
258 : 2010 : native_scope_flags = scope_flags;
259 : : } else {
260 : : // Load basic scope info for viper and asm.
261 : 216 : native_scope_flags = read_uint(reader);
262 [ + + ]: 216 : if (kind == MP_CODE_NATIVE_ASM) {
263 : 4 : native_n_pos_args = read_uint(reader);
264 : 4 : native_type_sig = read_uint(reader);
265 : : }
266 : : }
267 : : #endif
268 : : }
269 : :
270 : 3864 : size_t n_children = 0;
271 : 3864 : mp_raw_code_t **children = NULL;
272 : :
273 : : #if MICROPY_EMIT_MACHINE_CODE
274 : : // Load optional BSS/rodata for viper.
275 : 3864 : uint8_t *rodata = NULL;
276 : 3864 : uint8_t *bss = NULL;
277 [ + + ]: 3864 : if (kind == MP_CODE_NATIVE_VIPER) {
278 : 212 : size_t rodata_size = 0;
279 [ + + ]: 212 : if (native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA) {
280 : 4 : rodata_size = read_uint(reader);
281 : : }
282 : :
283 : 212 : size_t bss_size = 0;
284 [ + + ]: 212 : if (native_scope_flags & MP_SCOPE_FLAG_VIPERBSS) {
285 : 4 : bss_size = read_uint(reader);
286 : : }
287 : :
288 [ + + ]: 212 : if (rodata_size + bss_size != 0) {
289 : 4 : bss_size = (uintptr_t)MP_ALIGN(bss_size, sizeof(uintptr_t));
290 : 4 : uint8_t *data = m_new0(uint8_t, bss_size + rodata_size);
291 : 4 : bss = data;
292 : 4 : rodata = bss + bss_size;
293 [ + - ]: 4 : if (native_scope_flags & MP_SCOPE_FLAG_VIPERRODATA) {
294 : 4 : read_bytes(reader, rodata, rodata_size);
295 : : }
296 : :
297 : : // Viper code with BSS/rodata should not have any children.
298 : : // Reuse the children pointer to reference the BSS/rodata
299 : : // memory so that it is not reclaimed by the GC.
300 [ - + ]: 4 : assert(!has_children);
301 : : children = (void *)data;
302 : : }
303 : : }
304 : : #endif
305 : :
306 : : // Load children if any.
307 [ + + ]: 3864 : if (has_children) {
308 : 1113 : n_children = read_uint(reader);
309 : 1113 : children = m_new(mp_raw_code_t *, n_children + (kind == MP_CODE_NATIVE_PY));
310 [ + + ]: 3654 : for (size_t i = 0; i < n_children; ++i) {
311 : 2541 : children[i] = load_raw_code(reader, context);
312 : : }
313 : : }
314 : :
315 : : // Create raw_code and return it
316 : 3864 : mp_raw_code_t *rc = mp_emit_glue_new_raw_code();
317 [ + + ]: 3864 : if (kind == MP_CODE_BYTECODE) {
318 : 1854 : const byte *ip = fun_data;
319 [ + + ]: 2238 : MP_BC_PRELUDE_SIG_DECODE(ip);
320 : : // Assign bytecode to raw code object
321 : 1854 : mp_emit_glue_assign_bytecode(rc, fun_data,
322 : : #if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
323 : : fun_data_len,
324 : : #endif
325 : : children,
326 : : #if MICROPY_PERSISTENT_CODE_SAVE
327 : : n_children,
328 : : #endif
329 : : scope_flags);
330 : :
331 : : #if MICROPY_EMIT_MACHINE_CODE
332 : : } else {
333 : 2010 : const uint8_t *prelude_ptr;
334 : : #if MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE
335 : : if (kind == MP_CODE_NATIVE_PY) {
336 : : // Executable code cannot be accessed byte-wise on this architecture, so copy
337 : : // the prelude to a separate memory region that is byte-wise readable.
338 : : void *buf = fun_data + prelude_offset;
339 : : size_t n = fun_data_len - prelude_offset;
340 : : prelude_ptr = memcpy(m_new(uint8_t, n), buf, n);
341 : : }
342 : : #endif
343 : :
344 : : // Relocate and commit code to executable address space
345 : 2010 : reloc_info_t ri = {reader, context, rodata, bss};
346 : : #if defined(MP_PLAT_COMMIT_EXEC)
347 : : void *opt_ri = (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) ? &ri : NULL;
348 : : fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len, opt_ri);
349 : : #else
350 [ + + ]: 2010 : if (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) {
351 : : #if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE
352 : : // If native code needs relocations then it's not guaranteed that a pointer to
353 : : // the head of `buf` (containing the machine code) will be retained for the GC
354 : : // to trace. This is because native functions can start inside `buf` and so
355 : : // it's possible that the only GC-reachable pointers are pointers inside `buf`.
356 : : // So put this `buf` on a list of reachable root pointers.
357 : : if (MP_STATE_PORT(track_reloc_code_list) == MP_OBJ_NULL) {
358 : : MP_STATE_PORT(track_reloc_code_list) = mp_obj_new_list(0, NULL);
359 : : }
360 : : mp_obj_list_append(MP_STATE_PORT(track_reloc_code_list), MP_OBJ_FROM_PTR(fun_data));
361 : : #endif
362 : : // Do the relocations.
363 : 8 : mp_native_relocate(&ri, fun_data, (uintptr_t)fun_data);
364 : : }
365 : : #endif
366 : :
367 [ + + ]: 2010 : if (kind == MP_CODE_NATIVE_PY) {
368 : : #if !MICROPY_EMIT_NATIVE_PRELUDE_SEPARATE_FROM_MACHINE_CODE
369 : 1794 : prelude_ptr = fun_data + prelude_offset;
370 : : #endif
371 [ + + ]: 1794 : if (n_children == 0) {
372 : : children = (void *)prelude_ptr;
373 : : } else {
374 : 539 : children[n_children] = (void *)prelude_ptr;
375 : : }
376 : : }
377 : :
378 : : // Assign native code to raw code object
379 : 2010 : mp_emit_glue_assign_native(rc, kind,
380 : : fun_data, fun_data_len,
381 : : children,
382 : : #if MICROPY_PERSISTENT_CODE_SAVE
383 : : n_children,
384 : : prelude_offset,
385 : : #endif
386 : : native_scope_flags, native_n_pos_args, native_type_sig
387 : : );
388 : : #endif
389 : : }
390 : 3864 : return rc;
391 : : }
392 : :
393 : 1341 : void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *cm) {
394 : : // Set exception handler to close the reader if an exception is raised.
395 : 1341 : MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1(ctx, reader->close, reader->data);
396 : 1341 : nlr_push_jump_callback(&ctx.callback, mp_call_function_1_from_nlr_jump_callback);
397 : :
398 : 1341 : byte header[4];
399 : 1341 : read_bytes(reader, header, sizeof(header));
400 : 1341 : byte arch = MPY_FEATURE_DECODE_ARCH(header[2]);
401 [ + + ]: 1341 : if (header[0] != 'M'
402 [ + + ]: 1335 : || header[1] != MPY_VERSION
403 [ + + + - ]: 1327 : || (arch != MP_NATIVE_ARCH_NONE && MPY_FEATURE_DECODE_SUB_VERSION(header[2]) != MPY_SUB_VERSION)
404 [ - + ]: 1327 : || header[3] > MP_SMALL_INT_BITS) {
405 : 14 : mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file"));
406 : : }
407 [ + + ]: 1327 : if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) {
408 [ + + ]: 702 : if (!MPY_FEATURE_ARCH_TEST(arch)) {
409 : 4 : if (MPY_FEATURE_ARCH_TEST(MP_NATIVE_ARCH_NONE)) {
410 : : // On supported ports this can be resolved by enabling feature, eg
411 : : // mpconfigboard.h: MICROPY_EMIT_THUMB (1)
412 : : mp_raise_ValueError(MP_ERROR_TEXT("native code in .mpy unsupported"));
413 : : } else {
414 : 4 : mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy arch"));
415 : : }
416 : : }
417 : : }
418 : :
419 : 1323 : size_t n_qstr = read_uint(reader);
420 : 1323 : size_t n_obj = read_uint(reader);
421 : 1323 : mp_module_context_alloc_tables(cm->context, n_qstr, n_obj);
422 : :
423 : : // Load qstrs.
424 [ + + ]: 18833 : for (size_t i = 0; i < n_qstr; ++i) {
425 : 17510 : cm->context->constants.qstr_table[i] = load_qstr(reader);
426 : : }
427 : :
428 : : // Load constant objects.
429 [ + + ]: 7390 : for (size_t i = 0; i < n_obj; ++i) {
430 : 6067 : cm->context->constants.obj_table[i] = load_obj(reader);
431 : : }
432 : :
433 : : // Load top-level module.
434 : 1323 : cm->rc = load_raw_code(reader, cm->context);
435 : :
436 : : #if MICROPY_PERSISTENT_CODE_SAVE
437 : : cm->has_native = MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE;
438 : : cm->n_qstr = n_qstr;
439 : : cm->n_obj = n_obj;
440 : : #endif
441 : :
442 : : // Deregister exception handler and close the reader.
443 : 1323 : nlr_pop_jump_callback(true);
444 : 1323 : }
445 : :
446 : 0 : void mp_raw_code_load_mem(const byte *buf, size_t len, mp_compiled_module_t *context) {
447 : 0 : mp_reader_t reader;
448 : 0 : mp_reader_new_mem(&reader, buf, len, 0);
449 : 0 : mp_raw_code_load(&reader, context);
450 : 0 : }
451 : :
452 : : #if MICROPY_HAS_FILE_READER
453 : :
454 : 1341 : void mp_raw_code_load_file(const char *filename, mp_compiled_module_t *context) {
455 : 1341 : mp_reader_t reader;
456 : 1341 : mp_reader_new_file(&reader, filename);
457 : 1341 : mp_raw_code_load(&reader, context);
458 : 1323 : }
459 : :
460 : : #endif // MICROPY_HAS_FILE_READER
461 : :
462 : : #endif // MICROPY_PERSISTENT_CODE_LOAD
463 : :
464 : : #if MICROPY_PERSISTENT_CODE_SAVE
465 : :
466 : : #include "py/objstr.h"
467 : :
468 : : STATIC void mp_print_bytes(mp_print_t *print, const byte *data, size_t len) {
469 : : print->print_strn(print->data, (const char *)data, len);
470 : : }
471 : :
472 : : #define BYTES_FOR_INT ((MP_BYTES_PER_OBJ_WORD * 8 + 6) / 7)
473 : : STATIC void mp_print_uint(mp_print_t *print, size_t n) {
474 : : byte buf[BYTES_FOR_INT];
475 : : byte *p = buf + sizeof(buf);
476 : : *--p = n & 0x7f;
477 : : n >>= 7;
478 : : for (; n != 0; n >>= 7) {
479 : : *--p = 0x80 | (n & 0x7f);
480 : : }
481 : : print->print_strn(print->data, (char *)p, buf + sizeof(buf) - p);
482 : : }
483 : :
484 : : STATIC void save_qstr(mp_print_t *print, qstr qst) {
485 : : if (qst <= QSTR_LAST_STATIC) {
486 : : // encode static qstr
487 : : mp_print_uint(print, qst << 1 | 1);
488 : : return;
489 : : }
490 : : size_t len;
491 : : const byte *str = qstr_data(qst, &len);
492 : : mp_print_uint(print, len << 1);
493 : : mp_print_bytes(print, str, len + 1); // +1 to store null terminator
494 : : }
495 : :
496 : : STATIC void save_obj(mp_print_t *print, mp_obj_t o) {
497 : : #if MICROPY_EMIT_MACHINE_CODE
498 : : if (o == MP_OBJ_FROM_PTR(&mp_fun_table)) {
499 : : byte obj_type = MP_PERSISTENT_OBJ_FUN_TABLE;
500 : : mp_print_bytes(print, &obj_type, 1);
501 : : } else
502 : : #endif
503 : : if (mp_obj_is_str_or_bytes(o)) {
504 : : byte obj_type;
505 : : if (mp_obj_is_str(o)) {
506 : : obj_type = MP_PERSISTENT_OBJ_STR;
507 : : } else {
508 : : obj_type = MP_PERSISTENT_OBJ_BYTES;
509 : : }
510 : : size_t len;
511 : : const char *str = mp_obj_str_get_data(o, &len);
512 : : mp_print_bytes(print, &obj_type, 1);
513 : : mp_print_uint(print, len);
514 : : mp_print_bytes(print, (const byte *)str, len + 1); // +1 to store null terminator
515 : : } else if (o == mp_const_none) {
516 : : byte obj_type = MP_PERSISTENT_OBJ_NONE;
517 : : mp_print_bytes(print, &obj_type, 1);
518 : : } else if (o == mp_const_false) {
519 : : byte obj_type = MP_PERSISTENT_OBJ_FALSE;
520 : : mp_print_bytes(print, &obj_type, 1);
521 : : } else if (o == mp_const_true) {
522 : : byte obj_type = MP_PERSISTENT_OBJ_TRUE;
523 : : mp_print_bytes(print, &obj_type, 1);
524 : : } else if (MP_OBJ_TO_PTR(o) == &mp_const_ellipsis_obj) {
525 : : byte obj_type = MP_PERSISTENT_OBJ_ELLIPSIS;
526 : : mp_print_bytes(print, &obj_type, 1);
527 : : } else if (mp_obj_is_type(o, &mp_type_tuple)) {
528 : : size_t len;
529 : : mp_obj_t *items;
530 : : mp_obj_tuple_get(o, &len, &items);
531 : : byte obj_type = MP_PERSISTENT_OBJ_TUPLE;
532 : : mp_print_bytes(print, &obj_type, 1);
533 : : mp_print_uint(print, len);
534 : : for (size_t i = 0; i < len; ++i) {
535 : : save_obj(print, items[i]);
536 : : }
537 : : } else {
538 : : // we save numbers using a simplistic text representation
539 : : // TODO could be improved
540 : : byte obj_type;
541 : : if (mp_obj_is_int(o)) {
542 : : obj_type = MP_PERSISTENT_OBJ_INT;
543 : : #if MICROPY_PY_BUILTINS_COMPLEX
544 : : } else if (mp_obj_is_type(o, &mp_type_complex)) {
545 : : obj_type = MP_PERSISTENT_OBJ_COMPLEX;
546 : : #endif
547 : : } else {
548 : : assert(mp_obj_is_float(o));
549 : : obj_type = MP_PERSISTENT_OBJ_FLOAT;
550 : : }
551 : : vstr_t vstr;
552 : : mp_print_t pr;
553 : : vstr_init_print(&vstr, 10, &pr);
554 : : mp_obj_print_helper(&pr, o, PRINT_REPR);
555 : : mp_print_bytes(print, &obj_type, 1);
556 : : mp_print_uint(print, vstr.len);
557 : : mp_print_bytes(print, (const byte *)vstr.buf, vstr.len);
558 : : vstr_clear(&vstr);
559 : : }
560 : : }
561 : :
562 : : STATIC void save_raw_code(mp_print_t *print, const mp_raw_code_t *rc) {
563 : : // Save function kind and data length
564 : : mp_print_uint(print, (rc->fun_data_len << 3) | ((rc->n_children != 0) << 2) | (rc->kind - MP_CODE_BYTECODE));
565 : :
566 : : // Save function code.
567 : : mp_print_bytes(print, rc->fun_data, rc->fun_data_len);
568 : :
569 : : #if MICROPY_EMIT_MACHINE_CODE
570 : : if (rc->kind == MP_CODE_NATIVE_PY) {
571 : : // Save prelude size
572 : : mp_print_uint(print, rc->prelude_offset);
573 : : } else if (rc->kind == MP_CODE_NATIVE_VIPER || rc->kind == MP_CODE_NATIVE_ASM) {
574 : : // Save basic scope info for viper and asm
575 : : mp_print_uint(print, rc->scope_flags & MP_SCOPE_FLAG_ALL_SIG);
576 : : if (rc->kind == MP_CODE_NATIVE_ASM) {
577 : : mp_print_uint(print, rc->n_pos_args);
578 : : mp_print_uint(print, rc->type_sig);
579 : : }
580 : : }
581 : : #endif
582 : :
583 : : if (rc->n_children) {
584 : : mp_print_uint(print, rc->n_children);
585 : : for (size_t i = 0; i < rc->n_children; ++i) {
586 : : save_raw_code(print, rc->children[i]);
587 : : }
588 : : }
589 : : }
590 : :
591 : : void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) {
592 : : // header contains:
593 : : // byte 'M'
594 : : // byte version
595 : : // byte native arch (and sub-version if native)
596 : : // byte number of bits in a small int
597 : : byte header[4] = {
598 : : 'M',
599 : : MPY_VERSION,
600 : : cm->has_native ? MPY_FEATURE_ENCODE_SUB_VERSION(MPY_SUB_VERSION) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC) : 0,
601 : : #if MICROPY_DYNAMIC_COMPILER
602 : : mp_dynamic_compiler.small_int_bits,
603 : : #else
604 : : MP_SMALL_INT_BITS,
605 : : #endif
606 : : };
607 : : mp_print_bytes(print, header, sizeof(header));
608 : :
609 : : // Number of entries in constant table.
610 : : mp_print_uint(print, cm->n_qstr);
611 : : mp_print_uint(print, cm->n_obj);
612 : :
613 : : // Save qstrs.
614 : : for (size_t i = 0; i < cm->n_qstr; ++i) {
615 : : save_qstr(print, cm->context->constants.qstr_table[i]);
616 : : }
617 : :
618 : : // Save constant objects.
619 : : for (size_t i = 0; i < cm->n_obj; ++i) {
620 : : save_obj(print, (mp_obj_t)cm->context->constants.obj_table[i]);
621 : : }
622 : :
623 : : // Save outer raw code, which will save all its child raw codes.
624 : : save_raw_code(print, cm->rc);
625 : : }
626 : :
627 : : #if MICROPY_PERSISTENT_CODE_SAVE_FILE
628 : :
629 : : #include <unistd.h>
630 : : #include <sys/stat.h>
631 : : #include <fcntl.h>
632 : :
633 : : STATIC void fd_print_strn(void *env, const char *str, size_t len) {
634 : : int fd = (intptr_t)env;
635 : : MP_THREAD_GIL_EXIT();
636 : : ssize_t ret = write(fd, str, len);
637 : : MP_THREAD_GIL_ENTER();
638 : : (void)ret;
639 : : }
640 : :
641 : : void mp_raw_code_save_file(mp_compiled_module_t *cm, const char *filename) {
642 : : MP_THREAD_GIL_EXIT();
643 : : int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
644 : : MP_THREAD_GIL_ENTER();
645 : : if (fd < 0) {
646 : : mp_raise_OSError_with_filename(errno, filename);
647 : : }
648 : : mp_print_t fd_print = {(void *)(intptr_t)fd, fd_print_strn};
649 : : mp_raw_code_save(cm, &fd_print);
650 : : MP_THREAD_GIL_EXIT();
651 : : close(fd);
652 : : MP_THREAD_GIL_ENTER();
653 : : }
654 : :
655 : : #endif // MICROPY_PERSISTENT_CODE_SAVE_FILE
656 : :
657 : : #endif // MICROPY_PERSISTENT_CODE_SAVE
658 : :
659 : : #if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE
660 : : // An mp_obj_list_t that tracks relocated native code to prevent the GC from reclaiming them.
661 : : MP_REGISTER_ROOT_POINTER(mp_obj_t track_reloc_code_list);
662 : : #endif
|