Inside the ELF: What the ARM Assembler Really Generates on Raspberry Pi
In my previous post, I wrote a simple “Hello World!” in ARM assembly on the Raspberry Pi. This post will explore what happens when the assembler runs. We will have a look at the resulting binary object file that it creates, decode its raw bytes and dissect the ELF format by hand.
Executable and Linkable Format
Hereafter we are just copying the hexdump of the object file that was created in the previous post.
00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 01 00 28 00 01 00 00 00 00 00 00 00 00 00 00 00 |..(.............|
00000020 74 01 00 00 00 00 00 05 34 00 00 00 00 00 28 00 |t.......4.....(.|
00000030 09 00 08 00 01 00 a0 e3 20 10 9f e5 0c 20 a0 e3 |........ .... ..|
00000040 04 70 a0 e3 00 00 00 ef 01 70 a0 e3 00 00 00 ef |.p.......p......|
00000050 48 65 6c 6c 6f 2c 20 41 52 4d 21 0a 00 00 00 00 |Hello, ARM!.....|
00000060 1c 00 00 00 41 11 00 00 00 61 65 61 62 69 00 01 |....A....aeabi..|
00000070 07 00 00 00 08 01 00 00 00 00 00 00 00 00 00 00 |................|
00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000090 00 00 00 00 03 00 01 00 00 00 00 00 00 00 00 00 |................|
000000a0 00 00 00 00 03 00 03 00 00 00 00 00 00 00 00 00 |................|
000000b0 00 00 00 00 03 00 04 00 01 00 00 00 00 00 00 00 |................|
000000c0 00 00 00 00 00 00 01 00 04 00 00 00 1c 00 00 00 |................|
000000d0 00 00 00 00 00 00 01 00 08 00 00 00 1c 00 00 00 |................|
000000e0 00 00 00 00 00 00 01 00 08 00 00 00 2c 00 00 00 |............,...|
000000f0 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 |................|
00000100 00 00 00 00 03 00 05 00 0b 00 00 00 00 00 00 00 |................|
00000110 00 00 00 00 10 00 01 00 00 24 61 00 6d 73 67 00 |.........$a.msg.|
00000120 24 64 00 5f 73 74 61 72 74 00 00 00 2c 00 00 00 |$d._start...,...|
00000130 02 01 00 00 00 2e 73 79 6d 74 61 62 00 2e 73 74 |......symtab..st|
00000140 72 74 61 62 00 2e 73 68 73 74 72 74 61 62 00 2e |rtab..shstrtab..|
00000150 72 65 6c 2e 74 65 78 74 00 2e 64 61 74 61 00 2e |rel.text..data..|
00000160 62 73 73 00 2e 41 52 4d 2e 61 74 74 72 69 62 75 |bss..ARM.attribu|
00000170 74 65 73 00 00 00 00 00 00 00 00 00 00 00 00 00 |tes.............|
00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000190 00 00 00 00 00 00 00 00 00 00 00 00 1f 00 00 00 |................|
000001a0 01 00 00 00 06 00 00 00 00 00 00 00 34 00 00 00 |............4...|
000001b0 30 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 |0...............|
000001c0 00 00 00 00 1b 00 00 00 09 00 00 00 40 00 00 00 |............@...|
000001d0 00 00 00 00 2c 01 00 00 08 00 00 00 06 00 00 00 |....,...........|
000001e0 01 00 00 00 04 00 00 00 08 00 00 00 25 00 00 00 |............%...|
000001f0 01 00 00 00 03 00 00 00 00 00 00 00 64 00 00 00 |............d...|
00000200 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................|
00000210 00 00 00 00 2b 00 00 00 08 00 00 00 03 00 00 00 |....+...........|
00000220 00 00 00 00 64 00 00 00 00 00 00 00 00 00 00 00 |....d...........|
00000230 00 00 00 00 01 00 00 00 00 00 00 00 30 00 00 00 |............0...|
00000240 03 00 00 70 00 00 00 00 00 00 00 00 64 00 00 00 |...p........d...|
00000250 12 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................|
00000260 00 00 00 00 01 00 00 00 02 00 00 00 00 00 00 00 |................|
00000270 00 00 00 00 78 00 00 00 a0 00 00 00 07 00 00 00 |....x...........|
00000280 09 00 00 00 04 00 00 00 10 00 00 00 09 00 00 00 |................|
00000290 03 00 00 00 00 00 00 00 00 00 00 00 18 01 00 00 |................|
000002a0 12 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................|
000002b0 00 00 00 00 11 00 00 00 03 00 00 00 00 00 00 00 |................|
000002c0 00 00 00 00 34 01 00 00 40 00 00 00 00 00 00 00 |....4...@.......|
000002d0 00 00 00 00 01 00 00 00 00 00 00 00 |............|
000002dc
000002dc
After some googling, I found out that the object file is actually described through the ELF (Executable and Linking Format) specification, which in turn is part of the more broad Application Binary Interface (ABI) specification. The latter defines operating system and application interfaces that are necessary to construct an execution environment for applications. The most recent (well, referencing today 29/05/2025) ABI standard can be found here. More specifically, this is the System V ABI which is composed from 2 parts, the generic specification (the gABI) and the architecture specific extensions. Chapters 4 and 5 of the gABI have recently been updated and the updates can be found here. It is chapter 4 “Object files” that is of interest to get the parsing going!
The object file is composed of different structures, we’ll typically find the ELF header, section header table and sections. The ELF header provides some metadata on how to interprete the object file, the section header table is an array of section header entries that describe the sections and the sections themselves are the bits and bytes that contain the actual data. But we’ll need to start parsing the ELF header to get a better idea about the structure of the object file.
Manually parsing the ELF header
As can be found in Chapter 4, the ELF header has the following structure:
typedef struct {
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;
Each of the members will be discussed hereafter, but you can always reference the specification.
e_type
We see that the lengths in the header below are referenced as Elf32_Half, Elf32_Word, Elf32_Addr, Elf32_Off, it is interesting to know that these represent 2, 4, 4 and 4 bytes respectively. It makes sense to understand that Addr means that the value will signify an address and Off an offset.
Name | Length | Value | Interpretation |
---|---|---|---|
e_ident | 16 bytes | 0x7f 0x45 0x4c 0x46 0x01 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 | see below |
e_type | Elf32_Half | 0x00 0x01 | ET_REL = relocatable file |
e_machine | Elf32_Half | 0x00 0x28 | EM_ARM = Advanced RISC Machines ARM |
e_version | Elf32_Word | 0x00 0x00 0x00 0x01 | EV_CURRENT = Current Version |
e_entry | Elf32_Addr | 0x00 0x00 0x00 0x00 | Since this member holds zero, the file has no associated entry point |
e_phoff | Elf32_Off | 0x00 0x00 0x00 0x00 | Since this member holds zero, the file has no program header table |
e_shoff | Elf32_Off | 0x00 0x00 0x01 0x74 | Since this member is non-zero, it holds the section header table’s file offset in bytes |
e_flags | Elf32_Word | 0x05 0x00 0x00 0x00 | Flag names take the form of EF_machine_flag |
e_ehsize | Elf32_Half | 0x00 0x34 | This one indicates that the ELF header’s size is 52 bytes long |
e_phentsize | Elf32_Half | 0x00 0x00 | One entry in the file’s program header table measures 0 bytes |
e_phnum | Elf32_Half | 0x00 0x00 | There are 0 entries in the program header table. Since e_phnum is 0, there is no program header table. |
e_shentsize | Elf32_Half | 0x00 0x28 | A section header’s size is 40 bytes. A section header is one entry in the section header table and all entries are the same size. |
e_shnum | Elf32_Half | 0x00 0x09 | You will find 9 entries in the section header table. So the section header table entry is 360 bytes (e_shnum times e_shentsize). In case there is no section header table, this value should have been 0. |
e_shstrndx | Elf32_Half | 0x00 0x08 | This holds the section header table index of the entry associated with the section name string table. |
e_ident
Parsing this file, by hand for this first exercise, according to the updated chapter 4 of the general ABI specification, we notice the first 4 bytes indicating 0x7F, 0x45, 0x4C, 0x46. These are a so called “magic” number as they are just defined as such. The first byte will always be 0x7F, where the following 3 bytes represent the ASCII code for ELF. The fifth byte EU_CLASS is set to 0x01 representing the ELFCLASS32, which means we are dealing with 32-bit objects and supports machines with 32-bit architectures. The next byte encodes the EI_DATA, since the data is set to 0x01, it represents ELFDATA2LSB, meaning that the byte address 0 is on the left. So imagine we would have to store the hexstring 0x01020304, this would mean that the bytes will be stored as followed:
hexstring | 0x04 | 0x03 | 0x02 | 0x01 |
---|---|---|---|---|
address | 3 | 2 | 1 | 0 |
stored hexstring | 0x01 | 0x02 | 0x03 | 0x04 |
Next to EI_DATA, we have EI_VERSION encoded in the 7th byte, which is currently EV_CURRENT, encoded as 0x01. Following EI_VERSION, the EI_OSABI identifies the OS- of ABI-specific ELF extension used by this file. The 0x00 just declares that there are no extensions to be taken into account (or unspecified). EI_ABIVERSION also declares that this is unspecified. The rest of the bytes are then just padding bytes for this header.
Exploring the ELF Sections
The next important concept in the object file are sections since they hold most of the object file’s information. Every section is described through information in the section header table. The latter being an array of ELf32_Shdr structures (or ELf64_Shdr structures depending on the architecture). The ELF Header’s e_shoff, e-shnum and e_shentsize provides you some information how to find and interprete the object file’s section header table as they represent the byte offset of the section header table from the beginning of the file, the number of entries and the size of an entry respectively.
So referring to our parsed ELF header above, we already found that:
Name | Value | Interpretation |
---|---|---|
e_shoff | 0x00 0x00 0x01 0x74 | The section header table’s byte offset is 372 from the beginning of the file |
e_shnum | 0x00 0x09 | We will find 9 section header table entries |
e_shentsize | 0x00 0x28 | An entry of the section header table has 40 bytes |
So the section header table starts at byte 372 (0x174) and ends at byte 731 (= 372 + (9 * 40 -1)) (0x2DB). Which means that from our hexdump above, the section header table is the following (and I’ve replaced the byte values by XX for the bytes that are not part of the section header table):
00000170 XX XX XX XX 00 00 00 00 00 00 00 00 00 00 00 00 |tes.............|
00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000190 00 00 00 00 00 00 00 00 00 00 00 00 1f 00 00 00 |................|
000001a0 01 00 00 00 06 00 00 00 00 00 00 00 34 00 00 00 |............4...|
000001b0 30 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 |0...............|
000001c0 00 00 00 00 1b 00 00 00 09 00 00 00 40 00 00 00 |............@...|
000001d0 00 00 00 00 2c 01 00 00 08 00 00 00 06 00 00 00 |....,...........|
000001e0 01 00 00 00 04 00 00 00 08 00 00 00 25 00 00 00 |............%...|
000001f0 01 00 00 00 03 00 00 00 00 00 00 00 64 00 00 00 |............d...|
00000200 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................|
00000210 00 00 00 00 2b 00 00 00 08 00 00 00 03 00 00 00 |....+...........|
00000220 00 00 00 00 64 00 00 00 00 00 00 00 00 00 00 00 |....d...........|
00000230 00 00 00 00 01 00 00 00 00 00 00 00 30 00 00 00 |............0...|
00000240 03 00 00 70 00 00 00 00 00 00 00 00 64 00 00 00 |...p........d...|
00000250 12 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................|
00000260 00 00 00 00 01 00 00 00 02 00 00 00 00 00 00 00 |................|
00000270 00 00 00 00 78 00 00 00 a0 00 00 00 07 00 00 00 |....x...........|
00000280 09 00 00 00 04 00 00 00 10 00 00 00 09 00 00 00 |................|
00000290 03 00 00 00 00 00 00 00 00 00 00 00 18 01 00 00 |................|
000002a0 12 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................|
000002b0 00 00 00 00 11 00 00 00 03 00 00 00 00 00 00 00 |................|
000002c0 00 00 00 00 34 01 00 00 40 00 00 00 00 00 00 00 |....4...@.......|
000002d0 00 00 00 00 01 00 00 00 00 00 00 00 |............|
000002dc
Each section header entry follows this structure:
typedef struct {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr;
So, we have the following section header table entries:
Section header table entry 0 [0x174 - 0x19B]
00000170 XX XX XX XX 00 00 00 00 00 00 00 00 00 00 00 00 |tes.............|
00000180 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000190 00 00 00 00 00 00 00 00 00 00 00 00 XX XX XX XX |................|
Well, according to the spec, it seems that index 0, called SHN_UNDEF, is actually one of the special section indexes. It marks an undefined, missing, irrelevant or otherwise meaningless section reference. Given its filled with zeroes, I would currently go for an irrelevant section…
Section header table entry 1 [0x19C - 0x1C3]
00000190 XX XX XX XX XX XX XX XX XX XX XX XX 1f 00 00 00 |................|
000001a0 01 00 00 00 06 00 00 00 00 00 00 00 34 00 00 00 |............4...|
000001b0 30 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 |0...............|
000001c0 00 00 00 00 XX XX XX XX XX XX XX XX XX XX XX XX |............@...|
This is the first entry with some actual values. So trying to parse this according to the spec seems to result in the folowing:
Name | Length | Value | Interpretation |
---|---|---|---|
sh_name | Elf32_Word | 0x00 0x00 0x00 0x1f | The name is defined by the string at index 31 of the string table: .text |
sh_type | Elf32_Word | 0x00 0x00 0x00 0x01 | The type of this section is SHT_PROGBITS. A section holding information defined by the program. Format and meaning are determined solely by the program. |
sh_flags | Elf32_Word | 0x00 0x00 0x00 0x06 | This means that flags SHF_ALLOC (0x02) and SHF_EXECINSTR (0x04) are set. SHF_ALLOC indicates that section will occupy memory during process execution. The other flag indicates that the section contains executable machine instructions. |
sh_addr | Elf32_Addr | 0x00 0x00 0x00 0x00 | A 0 seems to indicate that the section will not appear in the memory image of a process |
sh_offset | Elf32_Off | 0x00 0x00 0x00 0x34 | This is the byte offset from the beginning of the file to the first byte in the section |
sh_size | Elf32_Word | 0x00 0x00 0x00 0x30 | Section size in bytes |
sh_link | Elf32_Word | 0x00 0x00 0x00 0x00 | Contains a section header table index link, interpretation depends on the section type. It does not seem relevant though for an sh_type of SHT_PROGBITS |
sh_info | Elf32_Word | 0x00 0x00 0x00 0x00 | Contains extra information whose interpretation depends on the section type. It does not seem relevant though for an sh_type of SHT_PROGBITS |
sh_addralign | Elf32_Word | 0x00 0x00 0x00 0x04 | Some sections have address alignment constraints. In this case, I’m not yet 100% certain what this value of 4 actually represents |
sh_entsize | Elf32_Word | 0x00 0x00 0x00 0x00 | The value of 0 indicates that the section does not hold a table of fixed-size entries |
Section [0x34 - 0x63]
Given the above section header table entry, the section is actually consisting out of the following bytes:
00000030 XX XX XX XX 01 00 a0 e3 20 10 9f e5 0c 20 a0 e3 |........ .... ..|
00000040 04 70 a0 e3 00 00 00 ef 01 70 a0 e3 00 00 00 ef |.p.......p......|
00000050 48 65 6c 6c 6f 2c 20 41 52 4d 21 0a 00 00 00 00 |Hello, ARM!.....|
00000060 1c 00 00 00 XX XX XX XX XX XX XX XX XX XX XX XX |....A....aeabi..|
Since .text indicates that the section holds the executable instructions of the program, I would expect that the section represents the binary instructions of the assembly code. But, I won’t deep dive into it at this point in time.
Section header table entry 2 [0x1C4 - 0x1EB]
000001c0 XX XX XX XX 1b 00 00 00 09 00 00 00 40 00 00 00 |............@...|
000001d0 00 00 00 00 2c 01 00 00 08 00 00 00 06 00 00 00 |....,...........|
000001e0 01 00 00 00 04 00 00 00 08 00 00 00 XX XX XX XX |............%...|
Name | Length | Value | Interpretation |
---|---|---|---|
sh_name | Elf32_Word | 0x00 0x00 0x00 0x1b | The name is defined by the string at index 27 of the string table: .rel.text |
sh_type | Elf32_Word | 0x00 0x00 0x00 0x09 | SHT_REL This section holds relocation entries without explicit addends. But, I’m not quite sure what this means, yet… |
sh_flags | Elf32_Word | 0x00 0x00 0x00 0x40 | SHF_INFO_LINK indicates that the sh_info field of this section header holds a section header table index |
sh_addr | Elf32_Addr | 0x00 0x00 0x00 0x00 | A 0 seems to indicate that the section will not appear in the memory image of a process |
sh_offset | Elf32_Off | 0x00 0x00 0x01 0x2C | This is the byte offset from the beginning of the file to the first byte in the section |
sh_size | Elf32_Word | 0x00 0x00 0x00 0x08 | Section size in bytes |
sh_link | Elf32_Word | 0x00 0x00 0x00 0x06 | This should represent the section header index of the associated symbol table. As defined by figure 4-12 in the spec |
sh_info | Elf32_Word | 0x00 0x00 0x00 0x01 | The section header index of the section to which the relocation applies. As defined by figure 4-12 in the spec |
sh_addralign | Elf32_Word | 0x00 0x00 0x00 0x04 | Some sections have address alignment constraints. In this case, I’m not yet 100% certain what this value of 4 actually represents |
sh_entsize | Elf32_Word | 0x00 0x00 0x00 0x08 | This section will hold a table of fixed-size entries, each entry will be 8 bytes |
Section [0x12C - 0x133]
00000120 XX XX XX XX XX XX XX XX XX XX XX XX 2c 00 00 00 |$d._start...,...|
00000130 02 01 00 00 XX XX XX XX XX XX XX XX XX XX XX XX |......symtab..st|
.rel.text indicates that this section is holding relocation information as defined here. Not further looking into this for the time being.
Section header table entry 3 [0x1EC - 0x213]
000001e0 XX XX XX XX XX XX XX XX XX XX XX XX 25 00 00 00 |............%...|
000001f0 01 00 00 00 03 00 00 00 00 00 00 00 64 00 00 00 |............d...|
00000200 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................|
00000210 00 00 00 00 XX XX XX XX XX XX XX XX XX XX XX XX |....+...........|
Name | Length | Value | Interpretation |
---|---|---|---|
sh_name | Elf32_Word | 0x00 0x00 0x00 0x25 | The name is defined by the string at index 37 of the string table: .data |
sh_type | Elf32_Word | 0x00 0x00 0x00 0x01 | SHT_PROGBITS A section holding information defined by the program. Format and meaning are determined solely by the program |
sh_flags | Elf32_Word | 0x00 0x00 0x00 0x03 | This means that flags SHF_WRITE (0x01) and SHF_ALLOC (0x02) are set. Meaning that the section contains data that should be writable during process execution and that the section occupies memory during process execution |
sh_addr | Elf32_Addr | 0x00 0x00 0x00 0x00 | A 0 seems to indicate that the section will not appear in the memory image of a process |
sh_offset | Elf32_Off | 0x00 0x00 0x00 0x64 | This is the byte offset from the beginning of the file to the first byte in the section |
sh_size | Elf32_Word | 0x00 0x00 0x00 0x00 | Section size in bytes |
sh_link | Elf32_Word | 0x00 0x00 0x00 0x00 | Contains a section header table index link, interpretation depends on the section type. It does not seem relevant though for an sh_type of SHT_PROGBITS |
sh_info | Elf32_Word | 0x00 0x00 0x00 0x00 | Contains a section header table index link, interpretation depends on the section type. It does not seem relevant though for an sh_type of SHT_PROGBITS |
sh_addralign | Elf32_Word | 0x00 0x00 0x00 0x01 | A value of 1 indicates that there are no alignment constraints |
sh_entsize | Elf32_Word | 0x00 0x00 0x00 0x00 | The value of 0 indicates that the section does not hold a table of fixed-size entries |
We do have to seem a section offset, however the size of the section appears to be 0. The .data section should also contain initialized data that contributes to the program’s memory image. Still puzzled somewhat why this section can have a size of 0 bytes in this case…
Section header table entry 4 [0x214 - 0x23B]
00000210 XX XX XX XX 2b 00 00 00 08 00 00 00 03 00 00 00 |....+...........|
00000220 00 00 00 00 64 00 00 00 00 00 00 00 00 00 00 00 |....d...........|
00000230 00 00 00 00 01 00 00 00 00 00 00 00 XX XX XX XX |............0...|
Name | Length | Value | Interpretation |
---|---|---|---|
sh_name | Elf32_Word | 0x00 0x00 0x00 0x2b | The name is defined by the string at index 43 of the string table: .bss |
sh_type | Elf32_Word | 0x00 0x00 0x00 0x08 | SHT_NOBITS A section that occupies no space in the file. |
sh_flags | Elf32_Word | 0x00 0x00 0x00 0x03 | This means that flags SHF_WRITE (0x01) and SHF_ALLOC (0x02) are set. Meaning that the section contains data that should be writable during process execution and that the section occupies memory during process execution |
sh_addr | Elf32_Addr | 0x00 0x00 0x00 0x00 | A 0 seems to indicate that the section will not appear in the memory image of a process |
sh_offset | Elf32_Off | 0x00 0x00 0x00 0x64 | Since this is a section of type SHT_NOBITS, this field contains the conceptual file offset |
sh_size | Elf32_Word | 0x00 0x00 0x00 0x00 | Section size in bytes |
sh_link | Elf32_Word | 0x00 0x00 0x00 0x00 | Contains a section header table index link, interpretation depends on the section type. It does not seem relevant though for an sh_type of SHT_NOBITS |
sh_info | Elf32_Word | 0x00 0x00 0x00 0x00 | Contains a section header table index link, interpretation depends on the section type. It does not seem relevant though for an sh_type of SHT_NOBITS |
sh_addralign | Elf32_Word | 0x00 0x00 0x00 0x01 | A value of 1 indicates that there are no alignment constraints |
sh_entsize | Elf32_Word | 0x00 0x00 0x00 0x00 | The value of 0 indicates that the section does not hold a table of fixed-size entries |
.bss sections hold uninitialized data that contribute to the program’s memory image. Data are initialized with zeroes when the program begins to run.
Section header table entry 5 [0x23C - 0x263]
00000230 XX XX XX XX XX XX XX XX XX XX XX XX 30 00 00 00 |............0...|
00000240 03 00 00 70 00 00 00 00 00 00 00 00 64 00 00 00 |...p........d...|
00000250 12 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................|
00000260 00 00 00 00 XX XX XX XX XX XX XX XX XX XX XX XX |................|
Name | Length | Value | Interpretation |
---|---|---|---|
sh_name | Elf32_Word | 0x00 0x00 0x00 0x30 | The name is defined by the string at index 48 of the string table: .ARM.attributes |
sh_type | Elf32_Word | 0x70 0x00 0x00 0x03 | This value is in between SHT_LOPROC (0x70000000) and SHT_HIPROC (0x7FFFFFFF), which are values reserved for processor-specific semantics |
sh_flags | Elf32_Word | 0x00 0x00 0x00 0x00 | No flags |
sh_addr | Elf32_Addr | 0x00 0x00 0x00 0x00 | A 0 seems to indicate that the section will not appear in the memory image of a process |
sh_offset | Elf32_Off | 0x00 0x00 0x00 0x64 | This is the byte offset from the beginning of the file to the first byte in the section |
sh_size | Elf32_Word | 0x00 0x00 0x00 0x12 | Section size in bytes |
sh_link | Elf32_Word | 0x00 0x00 0x00 0x00 | Contains a section header table index link, interpretation depends on the section type. It does not seem relevant for this header type |
sh_info | Elf32_Word | 0x00 0x00 0x00 0x00 | Contains a section header table index link, interpretation depends on the section type. It does not seem relevant for this header type |
sh_addralign | Elf32_Word | 0x00 0x00 0x00 0x01 | A value of 1 indicates that there are no alignment constraints |
sh_entsize | Elf32_Word | 0x00 0x00 0x00 0x00 | The value of 0 indicates that the section does not hold a table of fixed-size entries |
Section [0x64 - 0x75]
00000060 XX XX XX XX 41 11 00 00 00 61 65 61 62 69 00 01 |....A....aeabi..|
00000070 07 00 00 00 08 01 XX XX XX XX XX XX XX XX XX XX |................|
Not sure what to make from this section, yet…
Section header table entry 6 [0x264 - 0x28B]
00000260 XX XX XX XX 01 00 00 00 02 00 00 00 00 00 00 00 |................|
00000270 00 00 00 00 78 00 00 00 a0 00 00 00 07 00 00 00 |....x...........|
00000280 09 00 00 00 04 00 00 00 10 00 00 00 XX XX XX XX |................|
Name | Length | Value | Interpretation |
---|---|---|---|
sh_name | Elf32_Word | 0x00 0x00 0x00 0x01 | The name is defined by the string at index 1 of the string table: .symtab |
sh_type | Elf32_Word | 0x00 0x00 0x00 0x02 | SHT_SYMTAB Section that holds a symbol table |
sh_flags | Elf32_Word | 0x00 0x00 0x00 0x00 | No flags |
sh_addr | Elf32_Addr | 0x00 0x00 0x00 0x00 | A 0 seems to indicate that the section will not appear in the memory image of a process |
sh_offset | Elf32_Off | 0x00 0x00 0x00 0x78 | This is the byte offset from the beginning of the file to the first byte in the section |
sh_size | Elf32_Word | 0x00 0x00 0x00 0xa0 | Section size in bytes |
sh_link | Elf32_Word | 0x00 0x00 0x00 0x07 | The section header index of the associated string table |
sh_info | Elf32_Word | 0x00 0x00 0x00 0x09 | One greater than the symbol table index of the last local symbol (binding STB_LOCAL) |
sh_addralign | Elf32_Word | 0x00 0x00 0x00 0x04 | Some sections have address alignment constraints. In this case, I’m not yet 100% certain what this value of 4 actually represents |
sh_entsize | Elf32_Word | 0x00 0x00 0x00 0x10 | This section will hold a table of fixed-size entries, each entry will be 16 bytes |
Section [0x78 - 0x117]
00000070 XX XX XX XX XX XX XX XX 00 00 00 00 00 00 00 00 |................|
00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000090 00 00 00 00 03 00 01 00 00 00 00 00 00 00 00 00 |................|
000000a0 00 00 00 00 03 00 03 00 00 00 00 00 00 00 00 00 |................|
000000b0 00 00 00 00 03 00 04 00 01 00 00 00 00 00 00 00 |................|
000000c0 00 00 00 00 00 00 01 00 04 00 00 00 1c 00 00 00 |................|
000000d0 00 00 00 00 00 00 01 00 08 00 00 00 1c 00 00 00 |................|
000000e0 00 00 00 00 00 00 01 00 08 00 00 00 2c 00 00 00 |............,...|
000000f0 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 |................|
00000100 00 00 00 00 03 00 05 00 0b 00 00 00 00 00 00 00 |................|
00000110 00 00 00 00 10 00 01 00 XX XX XX XX XX XX XX XX |.........$a.msg.|
This section holds a symbol table and its definition can be found here.
Section header table entry 7 [0x28C - 0x2B3]
00000280 XX XX XX XX XX XX XX XX XX XX XX XX 09 00 00 00 |................|
00000290 03 00 00 00 00 00 00 00 00 00 00 00 18 01 00 00 |................|
000002a0 12 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 |................|
000002b0 00 00 00 00 XX XX XX XX XX XX XX XX XX XX XX XX |................|
Name | Length | Value | Interpretation |
---|---|---|---|
sh_name | Elf32_Word | 0x00 0x00 0x00 0x09 | The name is defined by the string at index 9 of the string table: .strtab |
sh_type | Elf32_Word | 0x00 0x00 0x00 0x03 | SHT_STRTAB This section holds a stringtable |
sh_flags | Elf32_Word | 0x00 0x00 0x00 0x00 | No flags |
sh_addr | Elf32_Addr | 0x00 0x00 0x00 0x00 | A 0 seems to indicate that the section will not appear in the memory image of a process |
sh_offset | Elf32_Off | 0x00 0x00 0x01 0x18 | This is the byte offset from the beginning of the file to the first byte in the section |
sh_size | Elf32_Word | 0x00 0x00 0x00 0x12 | Section size in bytes |
sh_link | Elf32_Word | 0x00 0x00 0x00 0x00 | Contains a section header table index link, interpretation depends on the section type. It does not seem relevant for this header type |
sh_info | Elf32_Word | 0x00 0x00 0x00 0x00 | Contains a section header table index link, interpretation depends on the section type. It does not seem relevant for this header type |
sh_addralign | Elf32_Word | 0x00 0x00 0x00 0x01 | A value of 1 indicates that there are no alignment constraints |
sh_entsize | Elf32_Word | 0x00 0x00 0x00 0x00 | The value of 0 indicates that the section does not hold a table of fixed-size entries |
Section [0x118 - 0x129] - string table
The .strtab section holds strings, typically, the names that are associated with the symbol table entries.
00000110 XX XX XX XX XX XX XX XX 00 24 61 00 6d 73 67 00 |.........$a.msg.|
00000120 24 64 00 5f 73 74 61 72 74 00 XX XX XX XX XX XX |$d._start...,...|
So in order to parse this, we slightly transform the above table into the following
Index | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 |
---|---|---|---|---|---|---|---|---|---|---|
0 | 00 | 24 | 61 | 00 | 6D | 73 | 67 | 00 | 24 | 64 |
10 | 00 | 5f | 73 | 74 | 61 | 72 | 74 | 00 |
Now, applying converting the hex values to their ASCII representation, we get:
Index | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 |
---|---|---|---|---|---|---|---|---|---|---|
0 | \0 | $ | a | \0 | m | s | g | \0 | $ | d |
10 | \0 | _ | s | t | a | r | t | \0 |
Resulting in the following string table
Index | String |
---|---|
0 | none |
1 | $a |
4 | msg |
8 | $d |
27 | .rel.text |
11 | _start |
Section header table entry 8 [0x2B4 - 0x2DB] - associated with section “string table”
We specifically notice how this section header table entry is indexed by e_shstrndx. By definition, this entry is associated with the section named “string table”.
000002b0 XX XX XX XX 11 00 00 00 03 00 00 00 00 00 00 00 |................|
000002c0 00 00 00 00 34 01 00 00 40 00 00 00 00 00 00 00 |....4...@.......|
000002d0 00 00 00 00 01 00 00 00 00 00 00 00 |............|
Lets have a look at what can be found inside this entry:
Name | Length | Value | Interpretation |
---|---|---|---|
sh_name | Elf32_Word | 0x00 0x00 0x00 0x11 | The name is defined by the string at index 17 of the string table: .shstrtab |
sh_type | Elf32_Word | 0x00 0x00 0x00 0x03 | SHT_STRTAB So the section will be holding a string table |
sh_flags | Elf32_Word | 0x00 0x00 0x00 0x00 | No flags |
sh_addr | Elf32_Addr | 0x00 0x00 0x00 0x00 | A 0 seems to indicate that the section will not appear in the memory image of a process |
sh_offset | Elf32_Off | 0x00 0x00 0x01 0x34 | This is the byte offset from the beginning of the file to the first byte in the section |
sh_size | Elf32_Word | 0x00 0x00 0x00 0x40 | Section size in bytes |
sh_link | Elf32_Word | 0x00 0x00 0x00 0x00 | Contains a section header table index link, interpretation depends on the section type. It does not seem relevant though for an sh_type of SHT_STRTAB |
sh_info | Elf32_Word | 0x00 0x00 0x00 0x00 | Contains extra information whose interpretation depends on the section type. It does not seem relevant though for an sh_type of SHT_STRTAB |
sh_addralign | Elf32_Word | 0x00 0x00 0x00 0x01 | A value of 1 indicates that there are no alignment constraints |
sh_entsize | Elf32_Word | 0x00 0x00 0x00 0x00 | The value of 0 indicates that the section does not hold a table of fixed-size entries |
Section [0x134 - 0x173] - section header string table
00000130 XX XX XX XX 00 2e 73 79 6d 74 61 62 00 2e 73 74 |......symtab..st|
00000140 72 74 61 62 00 2e 73 68 73 74 72 74 61 62 00 2e |rtab..shstrtab..|
00000150 72 65 6c 2e 74 65 78 74 00 2e 64 61 74 61 00 2e |rel.text..data..|
00000160 62 73 73 00 2e 41 52 4d 2e 61 74 74 72 69 62 75 |bss..ARM.attribu|
00000170 74 65 73 00 XX XX XX XX XX XX XX XX XX XX XX XX |tes.............|
However, in order to have a better undertanding of how the indexing into this table will work, I’ll slightly rewrite this as follows. It can be noticed how the indexes is determined by
Index | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 |
---|---|---|---|---|---|---|---|---|---|---|
0 | 00 | 2E | 73 | 79 | 6D | 74 | 61 | 62 | 00 | 2E |
10 | 73 | 74 | 72 | 74 | 61 | 62 | 00 | 2E | 73 | 68 |
20 | 73 | 74 | 72 | 74 | 61 | 62 | 00 | 2E | 72 | 65 |
30 | 6C | 2E | 74 | 65 | 78 | 74 | 00 | 2E | 64 | 61 |
40 | 74 | 61 | 00 | 2E | 62 | 73 | 73 | 00 | 2E | 41 |
50 | 52 | 4D | 2E | 61 | 74 | 74 | 72 | 69 | 62 | 75 |
60 | 74 | 65 | 73 | 00 |
And when converting the hex values into there ASCII representation, we have:
Index | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 | +8 | +9 |
---|---|---|---|---|---|---|---|---|---|---|
0 | \0 | . | s | y | m | t | a | b | \0 | . |
10 | s | t | r | t | a | b | \0 | . | s | h |
20 | s | t | r | t | a | b | \0 | . | r | e |
30 | l | . | t | e | x | t | \0 | . | d | a |
40 | t | a | \0 | . | b | s | s | \0 | . | A |
50 | R | M | . | a | t | t | r | i | b | u |
60 | t | e | s | \0 |
And in order to have an easy and quick look-up table:
Index | String |
---|---|
0 | none |
1 | .symtab |
9 | .strtab |
17 | .shstrtab |
27 | .rel.text |
31 | .text |
37 | .data |
43 | .bss |
48 | .ARM.attributes |
Please note how you can just index whereever you want in the string table to define a string. Here, at index 31, the string .text is actually a substring of .rel.text.
Verifying with Readelf!
Luckly there is no need to parse everything by hand, the readelf command parses the ELF file for you and provides some readable output.
readelf -elf hello.o
Returns the following
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: ARM
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 372 (bytes into file)
Flags: 0x5000000, Version5 EABI
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 9
Section header string table index: 8
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000034 000030 00 AX 0 0 4
[ 2] .rel.text REL 00000000 00012c 000008 08 I 6 1 4
[ 3] .data PROGBITS 00000000 000064 000000 00 WA 0 0 1
[ 4] .bss NOBITS 00000000 000064 000000 00 WA 0 0 1
[ 5] .ARM.attributes ARM_ATTRIBUTES 00000000 000064 000012 00 0 0 1
[ 6] .symtab SYMTAB 00000000 000078 0000a0 10 7 9 4
[ 7] .strtab STRTAB 00000000 000118 000012 00 0 0 1
[ 8] .shstrtab STRTAB 00000000 000134 000040 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
y (purecode), p (processor specific)
There are no section groups in this file.
There are no program headers in this file.
There is no dynamic section in this file.
Relocation section '.rel.text' at offset 0x12c contains 1 entry:
Offset Info Type Sym.Value Sym. Name
0000002c 00000102 R_ARM_ABS32 00000000 .text
There are no unwind sections in this file.
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 SECTION LOCAL DEFAULT 1
2: 00000000 0 SECTION LOCAL DEFAULT 3
3: 00000000 0 SECTION LOCAL DEFAULT 4
4: 00000000 0 NOTYPE LOCAL DEFAULT 1 $a
5: 0000001c 0 NOTYPE LOCAL DEFAULT 1 msg
6: 0000001c 0 NOTYPE LOCAL DEFAULT 1 $d
7: 0000002c 0 NOTYPE LOCAL DEFAULT 1 $d
8: 00000000 0 SECTION LOCAL DEFAULT 5
9: 00000000 0 NOTYPE GLOBAL DEFAULT 1 _start
No version information found in this file.
Attribute Section: aeabi
File Attributes
Tag_ARM_ISA_use: Yes
Which provides a nice summary of everything that was decoded by hand above!
Further research and study
Inspecting this object file has taken me deep into the ELF format. Even this only scratches the surface, it already helped me to have a better understanding of what the assembler is doing. I would certainly still like to have a better understanding of how the assembler instructions are converted in a bitwise representation and the resulting .text section. I’m curious how the GNU linker and GNU debugger will modify these binary files!
This was Steven documenting his embedded journeys! If you’ve got questions, want to share your own assembly experiences, or spot something I got wrong — drop a comment or reach out. I’d love to hear from you!
Coming up: The GNU linker, and the debugger! Or might as well get a bit more hands-on with a distance sensor :)
comments powered by Disqus