The ESP32 flash command, as executed by the Arduino IDE, seems to flash two bootloader files: boot_app0.bin at offset 0xe000 and bootloader_dio_80m.bin at offset 0x1000. I wonder what these two bootloader files actually do, and why there are two of them. Below I give some more information.
1. Context
I'm part of a team developing a new, free IDE for microcontrollers: Embeetle IDE. We're planning to support the ESP32 microcontroller family in the near future. Therefore, I'm now studying the ESP32 build system - both the ESP-IDF tool and the Arduino IDE approach to ESP32 projects.
2. Arduino IDE flash procedure for ESP32 projects
After building the .elf file, the Arduino IDE launches a command to convert it into a binary:
python esptool.py --chip esp32 elf2image
                  --flash_mode dio
                  --flash_freq 80m
                  --flash_size 4MB
                  -o /tmp/arduino_build_852524/WiFiScan.ino.bin
                  /tmp/arduino_build_852524/WiFiScan.ino.elf
Finally, this WiFiScan.ino.bin file is flashed to the board, alongside two bootloader files and the partitions table:
python esptool.py --chip esp32
                  --port /dev/ttyUSB0
                  --baud 921600
                  --before default_reset
                  --after hard_reset write_flash
                  -z
                  --flash_mode dio
                  --flash_freq 80m
                  --flash_size detect
                  0xe000  ~/.arduino15/packages/esp32/hardware/esp32/1.0.6/tools/partitions/boot_app0.bin
                  0x1000  ~/.arduino15/packages/esp32/hardware/esp32/1.0.6/tools/sdk/bin/bootloader_dio_80m.bin
                  0x10000 /tmp/arduino_build_852524/WiFiScan.ino.bin
                  0x8000  /tmp/arduino_build_852524/WiFiScan.ino.partitions.bin
The default partitions table, as used by Arduino IDE, looks like this (in csv format):
# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     0x9000,  0x5000,
otadata,  data, ota,     0xe000,  0x2000,
app0,     app,  ota_0,   0x10000, 0x140000,
app1,     app,  ota_1,   0x150000,0x140000,
spiffs,   data, spiffs,  0x290000,0x170000,
The binary equivalent of this csv-file gets flashed to address 0x8000. There are also two bootloader files being flashed to addresses 0xe000 and 0x1000 respectively (see next paragraph).
3. Bootloader files
The two bootloader files being flashed are:
# flashed at 0xe000
~/.arduino15/packages/esp32/hardware/esp32/1.0.6/tools/partitions/boot_app0.bin
and:
# flashed at 0x1000
~/.arduino15/packages/esp32/hardware/esp32/1.0.6/tools/sdk/bin/bootloader_dio_80m.bin
Question 1: What do these two bootloader files do?
It's also interesting to observe their locations. The first one, boot_app0.bin is located in a folder named 'partitions'. It sits there alongside several partition .csv files. Why? But maybe that gets clear when Question 1 is answered.
The other one, bootloader_dio_80m.bin is located in a folder named 'sdk/bin/' and sits alongside other files that all start their name with the 'bootloader_' prefix:
Question 2: As for the bootloader file flashed at address
0x1000, I think the'_40m'and'_80m'suffixes stand for the flash speed in MHz. But I've no idea what the'_dio','_dout'and'_qout'suffixes stand for.
Please enlighten me ^_^
Answer
Thanks to @Juraj, I now get a better insight into the startup procedure of an ESP32 chip. I believe it looks like this:
- FIRST STAGE BOOTLOADER: 
 The hardwired ROM-bootloader runs first. This first stage bootloader is outside the Flash memory and cannot be programmed. It loads the second stage bootloader (see next step).
- SECOND STAGE BOOTLOADER: 
 The first stage ROM-bootloader loads the second stage ESP-IDF Software bootloader at address- 0x1000in Flash. The code here is the- bootloader_dio_80m.binexecutable, which can be found in the- components/bootloaderdirectory of the ESP-IDF framework. This second stage bootloader reads the partition table found by default at offset- 0x8000. If OTA app partitions are found in the partition table, the bootloader consults the- ota_datapartition to determine which one should be booted.
- BOOT SWITCH 
 The- ota_datasection can be considered as merely a switch, located at- 0xe000in Flash. It determines if either- app0or- app1should boot. The switch itself is the- boot_app0.binbinary. As Juraj says, the 2kB size is also used to take notes during OTA flashing.
- APPLICATION 
 The application at- app0or- app1executes.
Thank you also for pointing me at these resources:

 
    