Microblaze ELF: A small look inside
This is a small reverse-engineering of the ELF file, as generated by Xilinx’ SDK for a simple standalone application targeted for the SP605 board.
ELF headers
Looking into the ELF file, we have something like this:
> mb-objdump --headers sdk/peripheral_tests_1/Debug/peripheral_tests_1.elf sdk/peripheral_tests_1/Debug/peripheral_tests_1.elf: file format elf32-microblazele Sections: Idx Name Size VMA LMA File off Algn 0 .vectors.reset 00000008 00000000 00000000 000000b4 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .vectors.sw_exception 00000008 00000008 00000008 000000bc 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 2 .vectors.interrupt 00000008 00000010 00000010 000000c4 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 3 .vectors.hw_exception 00000008 00000020 00000020 000000cc 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 4 .text 0000653c c0000000 c0000000 000000d4 2**2 CONTENTS, ALLOC, LOAD, CODE 5 .init 0000003c c000653c c000653c 00006610 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 6 .fini 0000001c c0006578 c0006578 0000664c 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 7 .ctors 00000008 c0006594 c0006594 00006668 2**2 CONTENTS, ALLOC, LOAD, DATA 8 .dtors 00000008 c000659c c000659c 00006670 2**2 CONTENTS, ALLOC, LOAD, DATA 9 .rodata 00000986 c00065a4 c00065a4 00006678 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 10 .sdata2 00000006 c0006f2a c0006f2a 00006ffe 2**0 ALLOC 11 .sbss2 00000000 c0006f30 c0006f30 000071d8 2**0 CONTENTS 12 .data 000001d0 c0006f30 c0006f30 00007000 2**2 CONTENTS, ALLOC, LOAD, DATA 13 .eh_frame 00000004 c0007100 c0007100 000071d0 2**2 CONTENTS, ALLOC, LOAD, DATA 14 .jcr 00000004 c0007104 c0007104 000071d4 2**2 CONTENTS, ALLOC, LOAD, DATA 15 .sdata 00000000 c0007108 c0007108 000071d8 2**0 CONTENTS 16 .sbss 00000000 c0007108 c0007108 000071d8 2**0 CONTENTS 17 .tdata 00000000 c0007108 c0007108 000071d8 2**0 CONTENTS 18 .tbss 00000000 c0007108 c0007108 000071d8 2**0 19 .bss 00000d78 c0007108 c0007108 000071d8 2**2 ALLOC 20 .heap 00000400 c0007e80 c0007e80 000071d8 2**0 ALLOC 21 .stack 00000400 c0008280 c0008280 000071d8 2**0 ALLOC 22 .debug_line 0000779f 00000000 00000000 000071d8 2**0 CONTENTS, READONLY, DEBUGGING 23 .debug_info 00008b11 00000000 00000000 0000e977 2**0 CONTENTS, READONLY, DEBUGGING 24 .debug_abbrev 000028e7 00000000 00000000 00017488 2**0 CONTENTS, READONLY, DEBUGGING 25 .debug_aranges 000006c0 00000000 00000000 00019d70 2**3 CONTENTS, READONLY, DEBUGGING 26 .debug_macinfo 0007f541 00000000 00000000 0001a430 2**0 CONTENTS, READONLY, DEBUGGING 27 .debug_frame 00000f10 00000000 00000000 00099974 2**2 CONTENTS, READONLY, DEBUGGING 28 .debug_loc 00003f80 00000000 00000000 0009a884 2**0 CONTENTS, READONLY, DEBUGGING 29 .debug_pubnames 00000fbe 00000000 00000000 0009e804 2**0 CONTENTS, READONLY, DEBUGGING 30 .debug_str 000018d5 00000000 00000000 0009f7c2 2**0 CONTENTS, READONLY, DEBUGGING 31 .debug_ranges 00000078 00000000 00000000 000a1097 2**0 CONTENTS, READONLY, DEBUGGING
Even though this is a lot of mumbo-jumbo, there are three main parts. The reset and interrupt vectors, around address zero, the main parts of the ELF (.text, .data and such) at Oxc0000000 and on, and the debug parts which have no memory allocation at all.
The reset branch to application
This is interesting to compare with the Microblaze’s memory map. It can be deduced from the .mhs file, but hey, the log file (with .log suffix) has this segment:
Address Map for Processor microblaze_0 (0000000000-0x00001fff) microblaze_0_d_bram_ctrl microblaze_0_dlmb (0000000000-0x00001fff) microblaze_0_i_bram_ctrl microblaze_0_ilmb (0x40000000-0x4000ffff) Push_Buttons_4Bits axi4lite_0 (0x40020000-0x4002ffff) LEDs_4Bits axi4lite_0 (0x40040000-0x4004ffff) DIP_Switches_4Bits axi4lite_0 (0x40600000-0x4060ffff) RS232_Uart_1 axi4lite_0 (0x40800000-0x4080ffff) IIC_SFP axi4lite_0 (0x40820000-0x4082ffff) IIC_EEPROM axi4lite_0 (0x40840000-0x4084ffff) IIC_DVI axi4lite_0 (0x40a00000-0x40a0ffff) SPI_FLASH axi4lite_0 (0x40e00000-0x40e0ffff) Ethernet_Lite axi4lite_0 (0x41800000-0x4180ffff) SysACE_CompactFlash axi4lite_0 (0x74800000-0x7480ffff) debug_module axi4lite_0 (0xc0000000-0xc7ffffff) MCB_DDR3 axi4_0
So obviously all the main ELF parts go directly to the DDR memory (that isn’t much of a surprise), and the reset/interrupt go to the internal block ram.
A quick disassembly reveals the gory details:
> mb-objdump --disassemble sdk/peripheral_tests_1/Debug/peripheral_tests_1.elf sdk/peripheral_tests_1/Debug/peripheral_tests_1.elf: file format elf32-microblazele Disassembly of section .vectors.reset: 00000000 <_start>: 0: b000c000 imm -16384 4: b8080000 brai 0 Disassembly of section .vectors.sw_exception: 00000008 <_vector_sw_exception>: 8: b000c000 imm -16384 c: b8081858 brai 6232 Disassembly of section .vectors.interrupt: 00000010 <_vector_interrupt>: 10: b000c000 imm -16384 14: b80818a4 brai 6308 Disassembly of section .vectors.hw_exception: 00000020 <_vector_hw_exception>: 20: b000c000 imm -16384 24: b8081870 brai 6256 Disassembly of section .text: c0000000 <_start1>: c0000000: b000c000 imm -16384 c0000004: 31a07108 addik r13, r0, 28936 c0000008: b000c000 imm -16384 c000000c: 30406f30 addik r2, r0, 28464 (... and it goes on and on ...)
So let’s look at the reset vector at address zero. The first IMM opcode loads C000 as the upper 16 bits for the command following, which is a branch immediate command. Together, they make a jump to Oxc000000. Likewise, the software exception jumps to Oxc0001858 and so on.
Since only the block RAM part can be included in the download.bit bitfile, only these jump vectors depend on the ELF file during the “Update bitfile” process. That’s why one gets away with not running this process, even when the ELF has been modified with a plain recompilation.
And now to the bootloop ELF
So what is the bootloop code doing? The headers are no more impressive than
> mb-objdump --headers bootloops/microblaze_0.elf bootloops/microblaze_0.elf: file format elf32-microblazele Sections: Idx Name Size VMA LMA File off Algn 0 .boot 00000004 00000000 00000000 00000074 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .text 00000000 00000000 00000000 00000074 2**0 CONTENTS, ALLOC, LOAD, READONLY, CODE 2 .data 00000000 00000000 00000000 00000074 2**0 CONTENTS, ALLOC, LOAD, DATA 3 .bss 00000000 00000000 00000000 00000078 2**0 ALLOC
Note the Size column: All entries are empty, except for the .boot section, which is four bytes small (one single instruction). That doesn’t leave room for sophisticated software, and the disassembly is indeed
> mb-objdump --disassemble bootloops/microblaze_0.elf bootloops/microblaze_0.elf: file format elf32-microblazele Disassembly of section .boot: 00000000 <_boot>: 0: b8000000 bri 0 // 0
Which is simply an endless loop. So they called it bootloop for a reason.
Reader Comments
Thanks quite helpful :)
Bill I am looking for an example to load the .elf via PCIe. I assume this would be via the registers within the Microblaze debug module that could be accessed via a PCIe BAR . This would be similar to Tandem configuration via the PCIe link but for the Microblaze . The other issue is how to load the various sections of the .elf into execution memory and how to initialize the data segments. I like your blog, can you say what WEB tools you use. Best Regards, Bob.