py/persistentcode: Add ability to relocate loaded native code.

Implements text, rodata and bss generalised relocations, as well as generic
qstr-object linking.  This allows importing dynamic native modules on all
supported architectures in a unified way.
pull/1/head
Damien George 2019-10-06 23:29:40 +11:00
parent b310930dba
commit b47e155bd0
9 changed files with 106 additions and 12 deletions

View File

@ -47,6 +47,7 @@
#include "py/nlr.h" #include "py/nlr.h"
#include "py/compile.h" #include "py/compile.h"
#include "py/runtime.h" #include "py/runtime.h"
#include "py/persistentcode.h"
#include "py/repl.h" #include "py/repl.h"
#include "py/gc.h" #include "py/gc.h"
#include "py/mphal.h" #include "py/mphal.h"
@ -173,12 +174,15 @@ void mbedtls_debug_set_threshold(int threshold) {
(void)threshold; (void)threshold;
} }
void *esp_native_code_commit(void *buf, size_t len) { void *esp_native_code_commit(void *buf, size_t len, void *reloc) {
len = (len + 3) & ~3; len = (len + 3) & ~3;
uint32_t *p = heap_caps_malloc(len, MALLOC_CAP_EXEC); uint32_t *p = heap_caps_malloc(len, MALLOC_CAP_EXEC);
if (p == NULL) { if (p == NULL) {
m_malloc_fail(len); m_malloc_fail(len);
} }
if (reloc) {
mp_native_relocate(reloc, buf, (uintptr_t)p);
}
memcpy(p, buf, len); memcpy(p, buf, len);
return p; return p;
} }

View File

@ -21,8 +21,6 @@
// emitters // emitters
#define MICROPY_PERSISTENT_CODE_LOAD (1) #define MICROPY_PERSISTENT_CODE_LOAD (1)
#define MICROPY_EMIT_XTENSAWIN (1) #define MICROPY_EMIT_XTENSAWIN (1)
void *esp_native_code_commit(void*, size_t);
#define MP_PLAT_COMMIT_EXEC(buf, len) esp_native_code_commit(buf, len)
// compiler configuration // compiler configuration
#define MICROPY_COMP_MODULE_CONST (1) #define MICROPY_COMP_MODULE_CONST (1)
@ -225,6 +223,8 @@ struct mp_bluetooth_nimble_root_pointers_t;
#define BYTES_PER_WORD (4) #define BYTES_PER_WORD (4)
#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void*)((mp_uint_t)(p))) #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void*)((mp_uint_t)(p)))
#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) #define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len)
void *esp_native_code_commit(void*, size_t, void*);
#define MP_PLAT_COMMIT_EXEC(buf, len, reloc) esp_native_code_commit(buf, len, reloc)
#define MP_SSIZE_MAX (0x7fffffff) #define MP_SSIZE_MAX (0x7fffffff)
// Note: these "critical nested" macros do not ensure cross-CPU exclusion, // Note: these "critical nested" macros do not ensure cross-CPU exclusion,

View File

@ -28,6 +28,7 @@
#include "py/gc.h" #include "py/gc.h"
#include "py/runtime.h" #include "py/runtime.h"
#include "py/persistentcode.h"
#include "py/mperrno.h" #include "py/mperrno.h"
#include "py/mphal.h" #include "py/mphal.h"
#include "drivers/dht/dht.h" #include "drivers/dht/dht.h"
@ -282,7 +283,7 @@ void esp_native_code_init(void) {
esp_native_code_erased = 0; esp_native_code_erased = 0;
} }
void *esp_native_code_commit(void *buf, size_t len) { void *esp_native_code_commit(void *buf, size_t len, void *reloc) {
//printf("COMMIT(buf=%p, len=%u, start=%08x, cur=%08x, end=%08x, erased=%08x)\n", buf, len, esp_native_code_start, esp_native_code_cur, esp_native_code_end, esp_native_code_erased); //printf("COMMIT(buf=%p, len=%u, start=%08x, cur=%08x, end=%08x, erased=%08x)\n", buf, len, esp_native_code_start, esp_native_code_cur, esp_native_code_end, esp_native_code_erased);
len = (len + 3) & ~3; len = (len + 3) & ~3;
@ -294,6 +295,14 @@ void *esp_native_code_commit(void *buf, size_t len) {
void *dest; void *dest;
if (esp_native_code_location == ESP_NATIVE_CODE_IRAM1) { if (esp_native_code_location == ESP_NATIVE_CODE_IRAM1) {
dest = (void*)esp_native_code_cur; dest = (void*)esp_native_code_cur;
} else {
dest = (void*)(FLASH_START + esp_native_code_cur);
}
if (reloc) {
mp_native_relocate(reloc, buf, (uintptr_t)dest);
}
if (esp_native_code_location == ESP_NATIVE_CODE_IRAM1) {
memcpy(dest, buf, len); memcpy(dest, buf, len);
} else { } else {
SpiFlashOpResult res; SpiFlashOpResult res;
@ -313,7 +322,6 @@ void *esp_native_code_commit(void *buf, size_t len) {
if (res != SPI_FLASH_RESULT_OK) { if (res != SPI_FLASH_RESULT_OK) {
mp_raise_OSError(res == SPI_FLASH_RESULT_TIMEOUT ? MP_ETIMEDOUT : MP_EIO); mp_raise_OSError(res == SPI_FLASH_RESULT_TIMEOUT ? MP_ETIMEDOUT : MP_EIO);
} }
dest = (void*)(FLASH_START + esp_native_code_cur);
} }
esp_native_code_cur += len; esp_native_code_cur += len;

View File

@ -131,8 +131,8 @@ typedef uint32_t sys_prot_t; // for modlwip
#include <sys/types.h> #include <sys/types.h>
#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) #define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len)
void *esp_native_code_commit(void*, size_t); void *esp_native_code_commit(void*, size_t, void*);
#define MP_PLAT_COMMIT_EXEC(buf, len) esp_native_code_commit(buf, len) #define MP_PLAT_COMMIT_EXEC(buf, len, reloc) esp_native_code_commit(buf, len, reloc)
// printer for debugging output, goes to UART only // printer for debugging output, goes to UART only
extern const struct _mp_print_t mp_debug_print; extern const struct _mp_print_t mp_debug_print;

View File

@ -60,7 +60,7 @@ static inline size_t mp_asm_base_get_code_size(mp_asm_base_t *as) {
static inline void *mp_asm_base_get_code(mp_asm_base_t *as) { static inline void *mp_asm_base_get_code(mp_asm_base_t *as) {
#if defined(MP_PLAT_COMMIT_EXEC) #if defined(MP_PLAT_COMMIT_EXEC)
return MP_PLAT_COMMIT_EXEC(as->code_base, as->code_size); return MP_PLAT_COMMIT_EXEC(as->code_base, as->code_size, NULL);
#else #else
return as->code_base; return as->code_base;
#endif #endif

View File

@ -74,7 +74,7 @@
#define MP_BC_PRELUDE_SIG_ENCODE(S, E, scope, out_byte, out_env) \ #define MP_BC_PRELUDE_SIG_ENCODE(S, E, scope, out_byte, out_env) \
do { \ do { \
/*// Get values to store in prelude */ \ /*// Get values to store in prelude */ \
size_t F = scope->scope_flags & 0x0f; /* only need to store lower 4 flag bits */ \ size_t F = scope->scope_flags & MP_SCOPE_FLAG_ALL_SIG; \
size_t A = scope->num_pos_args; \ size_t A = scope->num_pos_args; \
size_t K = scope->num_kwonly_args; \ size_t K = scope->num_kwonly_args; \
size_t D = scope->num_def_pos_args; \ size_t D = scope->num_def_pos_args; \

View File

@ -33,6 +33,7 @@
#include "py/emitglue.h" #include "py/emitglue.h"
#include "py/persistentcode.h" #include "py/persistentcode.h"
#include "py/bc0.h" #include "py/bc0.h"
#include "py/objstr.h"
#if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE #if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
@ -145,8 +146,16 @@ STATIC byte *extract_prelude(const byte **ip, bytecode_prelude_t *prelude) {
#include "py/parsenum.h" #include "py/parsenum.h"
STATIC int read_byte(mp_reader_t *reader);
STATIC size_t read_uint(mp_reader_t *reader, byte **out);
#if MICROPY_EMIT_MACHINE_CODE #if MICROPY_EMIT_MACHINE_CODE
typedef struct _reloc_info_t {
mp_reader_t *reader;
mp_uint_t *const_table;
} reloc_info_t;
#if MICROPY_EMIT_THUMB #if MICROPY_EMIT_THUMB
STATIC void asm_thumb_rewrite_mov(uint8_t *pc, uint16_t val) { STATIC void asm_thumb_rewrite_mov(uint8_t *pc, uint16_t val) {
// high part // high part
@ -179,6 +188,52 @@ STATIC void arch_link_qstr(uint8_t *pc, bool is_obj, qstr qst) {
#endif #endif
} }
void mp_native_relocate(void *ri_in, uint8_t *text, uintptr_t reloc_text) {
// Relocate native code
reloc_info_t *ri = ri_in;
uint8_t op;
uintptr_t *addr_to_adjust = NULL;
while ((op = read_byte(ri->reader)) != 0xff) {
if (op & 1) {
// Point to new location to make adjustments
size_t addr = read_uint(ri->reader, NULL);
if ((addr & 1) == 0) {
// Point to somewhere in text
addr_to_adjust = &((uintptr_t*)text)[addr >> 1];
} else {
// Point to somewhere in rodata
addr_to_adjust = &((uintptr_t*)ri->const_table[1])[addr >> 1];
}
}
op >>= 1;
uintptr_t dest;
size_t n = 1;
if (op <= 5) {
if (op & 1) {
// Read in number of adjustments to make
n = read_uint(ri->reader, NULL);
}
op >>= 1;
if (op == 0) {
// Destination is text
dest = reloc_text;
} else {
// Destination is rodata (op=1) or bss (op=1 if no rodata, else op=2)
dest = ri->const_table[op];
}
} else if (op == 6) {
// Destination is mp_fun_table itself
dest = (uintptr_t)&mp_fun_table;
} else {
// Destination is an entry in mp_fun_table
dest = ((uintptr_t*)&mp_fun_table)[op - 7];
}
while (n--) {
*addr_to_adjust++ += dest;
}
}
}
#endif #endif
STATIC int read_byte(mp_reader_t *reader) { STATIC int read_byte(mp_reader_t *reader) {
@ -340,6 +395,9 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) {
// Generic 16-bit link // Generic 16-bit link
dest[0] = qst & 0xff; dest[0] = qst & 0xff;
dest[1] = (qst >> 8) & 0xff; dest[1] = (qst >> 8) & 0xff;
} else if ((off & 3) == 3) {
// Generic, aligned qstr-object link
*(mp_obj_t*)dest = MP_OBJ_NEW_QSTR(qst);
} else { } else {
// Architecture-specific link // Architecture-specific link
arch_link_qstr(dest, (off & 3) == 2, qst); arch_link_qstr(dest, (off & 3) == 2, qst);
@ -423,8 +481,26 @@ STATIC mp_raw_code_t *load_raw_code(mp_reader_t *reader, qstr_window_t *qw) {
#if MICROPY_EMIT_MACHINE_CODE #if MICROPY_EMIT_MACHINE_CODE
} else { } else {
mp_uint_t *ct = &const_table[1];
if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRODATA) {
size_t size = read_uint(reader, NULL);
uint8_t *rodata = m_new(uint8_t, size);
read_bytes(reader, rodata, size);
*ct++ = (uintptr_t)rodata;
}
if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERBSS) {
size_t size = read_uint(reader, NULL);
uint8_t *bss = m_new0(uint8_t, size);
*ct++ = (uintptr_t)bss;
}
reloc_info_t ri = {reader, const_table};
#if defined(MP_PLAT_COMMIT_EXEC) #if defined(MP_PLAT_COMMIT_EXEC)
fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len); void *opt_ri = (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRELOC) ? &ri : NULL;
fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len, opt_ri);
#else
if (prelude.scope_flags & MP_SCOPE_FLAG_VIPERRELOC) {
mp_native_relocate(&ri, fun_data, (uintptr_t)fun_data);
}
#endif #endif
mp_emit_glue_assign_native(rc, kind, mp_emit_glue_assign_native(rc, kind,
@ -624,7 +700,7 @@ STATIC void save_raw_code(mp_print_t *print, mp_raw_code_t *rc, qstr_window_t *q
save_prelude_qstrs(print, qstr_window, ip_info); save_prelude_qstrs(print, qstr_window, ip_info);
} else { } else {
// Save basic scope info for viper and asm // Save basic scope info for viper and asm
mp_print_uint(print, rc->scope_flags); mp_print_uint(print, rc->scope_flags & MP_SCOPE_FLAG_ALL_SIG);
prelude.n_pos_args = 0; prelude.n_pos_args = 0;
prelude.n_kwonly_args = 0; prelude.n_kwonly_args = 0;
if (rc->kind == MP_CODE_NATIVE_ASM) { if (rc->kind == MP_CODE_NATIVE_ASM) {

View File

@ -95,4 +95,6 @@ mp_raw_code_t *mp_raw_code_load_file(const char *filename);
void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print); void mp_raw_code_save(mp_raw_code_t *rc, mp_print_t *print);
void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename); void mp_raw_code_save_file(mp_raw_code_t *rc, const char *filename);
void mp_native_relocate(void *reloc, uint8_t *text, uintptr_t reloc_text);
#endif // MICROPY_INCLUDED_PY_PERSISTENTCODE_H #endif // MICROPY_INCLUDED_PY_PERSISTENTCODE_H

View File

@ -28,13 +28,17 @@
// The first four must fit in 8 bits, see emitbc.c // The first four must fit in 8 bits, see emitbc.c
// The remaining must fit in 16 bits, see scope.h // The remaining must fit in 16 bits, see scope.h
#define MP_SCOPE_FLAG_ALL_SIG (0x0f)
#define MP_SCOPE_FLAG_GENERATOR (0x01) #define MP_SCOPE_FLAG_GENERATOR (0x01)
#define MP_SCOPE_FLAG_VARKEYWORDS (0x02) #define MP_SCOPE_FLAG_VARKEYWORDS (0x02)
#define MP_SCOPE_FLAG_VARARGS (0x04) #define MP_SCOPE_FLAG_VARARGS (0x04)
#define MP_SCOPE_FLAG_DEFKWARGS (0x08) #define MP_SCOPE_FLAG_DEFKWARGS (0x08)
#define MP_SCOPE_FLAG_REFGLOBALS (0x10) // used only if native emitter enabled #define MP_SCOPE_FLAG_REFGLOBALS (0x10) // used only if native emitter enabled
#define MP_SCOPE_FLAG_HASCONSTS (0x20) // used only if native emitter enabled #define MP_SCOPE_FLAG_HASCONSTS (0x20) // used only if native emitter enabled
#define MP_SCOPE_FLAG_VIPERRET_POS (6) // 3 bits used for viper return type #define MP_SCOPE_FLAG_VIPERRET_POS (6) // 3 bits used for viper return type, to pass from compiler to native emitter
#define MP_SCOPE_FLAG_VIPERRELOC (0x10) // used only when loading viper from .mpy
#define MP_SCOPE_FLAG_VIPERRODATA (0x20) // used only when loading viper from .mpy
#define MP_SCOPE_FLAG_VIPERBSS (0x40) // used only when loading viper from .mpy
// types for native (viper) function signature // types for native (viper) function signature
#define MP_NATIVE_TYPE_OBJ (0x00) #define MP_NATIVE_TYPE_OBJ (0x00)