I have commented about this on Gitter already, but will post my thoughts here as well, for the folks who are not on Gitter.
As a general rule, when you see an issue, try to diagnose it from "first principles".
Exception output says that CPU raised exception 0 at PC 0x402095f8. You have correctly checked the exception list for cause of exception 0 — it means that the instruction was invalid. But don't stop there
First do
xtensa-lx106-elf-objdump -d file.elf, and verify that the instruction at this address is in fact valid and objdump decodes it (i'm pretty sure it is valid).
So, if instruction is valid, why does CPU complain about invalid instruction? Well, this instruction is in flash, and CPU accesses instructions in flash memory through a cache. This cache is a piece of hardware which knows how to read data from flash memory, and return it to the CPU as if it was in some RAM. Because it is a cache, it also uses a piece of RAM (32k of it) to store instructions which get requested by the CPU frequently.
Because this piece of hardware talks to the SPI flash chip on its own, every time we want to read or write flash from our program we need to disable cache. Otherwise flash commands issued by code can get intermingled with commands issued by cache hardware, and this will cause read and write errors. So, each time SPIFFS needs to read or write something, flash cache is disabled. When it is disabled, it will always return 0x000000 as an instruction word, which is an illegal instruction for the CPU. All this means that if you try to run code located in Flash while flash read or write is in progress, this will cause exception 0.
Now, back to the original issue. When reading or writing flash from SPIFFS, i mask all interrupts except for SPI interrupt. See spiffs_hal.cpp file. I have tried masking SPI interrupt as well, but this seems to break the actual function which reads or writes flash. So extra care is needed in SPISlave to check if the ISR arrived when flash cache is disabled. If it is, SPISlave may choose to bail out of the ISR handler.
It would be also nice to dig why masking SPI interrupt breaks flash read/write routines. I suppose there may be a bug in SPIRead and/or SPIWrite ROM functions.