Repository navigation
Conversation
|
@dpgeorge can you take a look at this PR? |
Can you please elaborate on this point? Even if the xtensa compiler doesn't support |
|
Yes. As an example, here is modx.elf from modx.c with almost everything commented out, except adding the add1 function using mp_store_global: The first This pointer is loaded in the Translated to ARM assembly this Note that there is also a relocation emitted, the important bits are: There is almost no documentation about the various relocations on the internet, but from what I found this essentially means "take the address of the target and add the program counter", which of course is only known at runtime. I've tried all possible options (-fpic, -fPIC, -fpie, -fPIE, no option) and they all produced the exact same .o and .elf file (the same md5sum). So it appears like support is non-existent in gcc. I even looked through the ISA documentation and couldn't find a reference to a program counter register. It appears like the Xtensa ISA itself doesn't support position-independent execution at all. Summary: I cannot find a way to avoid runtime relocations with the given C code. |
|
Thanks for the detailed reply. I now understand the problem a bit better. As a simple test I tried compiling the esp8266 port with "-fPIC" and it did produce different code... but it still used callx0 in some places, which (as you point out) does an absolute call. Did you try compiling without the |
|
The code is already compiled without |
|
The problem is that It may be possible to somehow add the code base address manually to the relative function pointer (e.g. within |
Right. On archs that do support -fPIC this absolute address is computed using PC-relative instructions (or similar) which means that the location of the code can change from run to run.
This would be like implementing a poor-mans -fPIC in the actual C code, using lots of macros/tricks. If we can't rely on -fPIC working for any given arch then it looks like we want to go down the path of allowing relocations in native code, and linking when the native .mpy file is loaded. One of the benefits of doing it this way is that the native code is smaller because PIC adds overhead. |
Saves 16 bytes of code on a Cortex M0 and 7 bytes in the .mpy file.
53bc654 to
ac022b7
Compare
The ESP8266 doesn't seem to support truly position-independent code so we have to adjust a few pointers after loading of the .mpy file. This requires reading .rela relocations and adding some code to patch those pointers on import. Also, the ESP8266 doesn't use MP_PLAT_ALLOC_EXEC but MP_PLAT_COMMIT_EXEC so we have to dance around with allocation a bit.
ac022b7 to
08992f4
Compare
|
Rebased the commits on dpgeorge:dynamic-native-modules-v2.
Yeah, I'm afraid there is no other way.
True, but the overhead isn't as big as you might think. I've extended relocation support to Thumb and the result is a I think there are other more interesting benefits of relocations, as I (and you) have discussed before. |
Yes. So maybe it's time to make a list (based on those discussions) of pros/cons for relocation vs "PIC". Then we can make a decision and move to implement it. |
|
PIC vs relocations, as I see it. Advantages of PIC:
Advantages of relocations:
Regarding the first bullet point: so far I haven't seen a concrete use case for direct execution from flash (without loading it first). Either the module is stored on a filesystem (which does not allow direct execution due to fragmentation) or the module is built in the firmware, in which case there is no reason to store it as .mpy file instead of a built in C module (like It may also be possible to only use relocations on Xtensa, keeping PIC code on all other architectures. This avoids the PIC workarounds but does not give all other advantages. |
|
In the end I did go with relocations, although only in a simple form in that there is a single GOT which is relocated just by adding a constant (pointer to the start of the .text section when loaded) to each entry. See the result at micropython#5083 . That implements a full linker to turn .o into .mpy and supports x86, x64, ARM Thumb and xtensa (esp8266 and esp32). |
asan considers that memcmp(p, q, N) is permitted to access N bytes at each
of p and q, even for values of p and q that have a difference earlier.
Accessing additional values is frequently done in practice, reading 4 or
more bytes from each input at a time for efficiency, so when completing
"non_exist<TAB>" in the repl, this causes a diagnostic:
==16938==ERROR: AddressSanitizer: global-buffer-overflow on
address 0x555555cd8dc8 at pc 0x7ffff726457b bp 0x7fffffffda20 sp 0x7fff
READ of size 9 at 0x555555cd8dc8 thread T0
#0 0x7ffff726457a (/usr/lib/x86_64-linux-gnu/libasan.so.5+0xb857a)
#1 0x555555b0e82a in mp_repl_autocomplete ../../py/repl.c:301
#2 0x555555c89585 in readline_process_char ../../lib/mp-readline/re
#3 0x555555c8ac6e in readline ../../lib/mp-readline/readline.c:513
#4 0x555555b8dcbd in do_repl /home/jepler/src/micropython/ports/uni
#5 0x555555b90859 in main_ /home/jepler/src/micropython/ports/unix/
#6 0x555555b90a3a in main /home/jepler/src/micropython/ports/unix/m
#7 0x7ffff619a09a in __libc_start_main ../csu/libc-start.c:308
#8 0x55555595fd69 in _start (/home/jepler/src/micropython/ports/uni
0x555555cd8dc8 is located 0 bytes to the right of global variable
'import_str' defined in '../../py/repl.c:285:23' (0x555555cd8dc0) of
size 8
'import_str' is ascii string 'import '
Signed-off-by: Jeff Epler <[email protected]>
It appears like the ESP8266 doesn't support position-independent code, which is currently a requirement for persistent native. Additionally, the call to init() may not be the first address but some address further into the code. So this needs to be fixed if we want ESP8266 support. The latter is quite easy to fix (just add the init() offset to the .mpy file) but the former is a bit more difficult: I can't think of a way that doesn't require runtime relocations.
So to fix that, I've implemented an ELF reader (which I had already written) that adjusts some relative pointers in the .mpy file and also added a bit of code to patch the code with the correct addresses. All in all, I think this is the most minimal change required for ESP8266 support.
I could have used an ELF library (there are multiple), but to reduce dependencies I've chosen to implement it myself. If you'd rather use a library that's also fine by me. Also, the layout of the code may be improved, by extracting the ELF parsing logic into a separate Python module.
A couple of notes on the feature flags:
MICROPY_PY_BUILTINS_SLICEandMICROPY_PY_BUILTINS_SEThave to be listed inMPY_FEATURE_FLAGS: they adjust the size of thefun_tableso a different configuration may do something unintended (call the wrong method, etc.).MICROPY_OBJ_REPRshould also be listed in there (taking up 2 bits). I noticed this because qstr values were wrong on the ESP8266 before adjusting it inexamples/modx/mpconfigport.h.examples/modx/mpconfigport.hto get it to load .mpy files, namelyMICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE. I haven't yet implemented this switch in the Makefile. Similar manual changes were required for the nRF51822 (Cortex-M0).