Embedded Journeys

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.

NameLengthValueInterpretation
e_ident16 bytes0x7f 0x45 0x4c 0x46 0x01 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00see below
e_typeElf32_Half0x00 0x01ET_REL = relocatable file
e_machineElf32_Half0x00 0x28EM_ARM = Advanced RISC Machines ARM
e_versionElf32_Word0x00 0x00 0x00 0x01EV_CURRENT = Current Version
e_entryElf32_Addr0x00 0x00 0x00 0x00Since this member holds zero, the file has no associated entry point
e_phoffElf32_Off0x00 0x00 0x00 0x00Since this member holds zero, the file has no program header table
e_shoffElf32_Off0x00 0x00 0x01 0x74Since this member is non-zero, it holds the section header table’s file offset in bytes
e_flagsElf32_Word0x05 0x00 0x00 0x00Flag names take the form of EF_machine_flag
e_ehsizeElf32_Half0x00 0x34This one indicates that the ELF header’s size is 52 bytes long
e_phentsizeElf32_Half0x00 0x00One entry in the file’s program header table measures 0 bytes
e_phnumElf32_Half0x00 0x00There are 0 entries in the program header table. Since e_phnum is 0, there is no program header table.
e_shentsizeElf32_Half0x00 0x28A 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_shnumElf32_Half0x00 0x09You 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_shstrndxElf32_Half0x00 0x08This 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:

hexstring0x040x030x020x01
address3210
stored hexstring0x010x020x030x04

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:

NameValueInterpretation
e_shoff0x00 0x00 0x01 0x74The section header table’s byte offset is 372 from the beginning of the file
e_shnum0x00 0x09We will find 9 section header table entries
e_shentsize0x00 0x28An 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:

NameLengthValueInterpretation
sh_nameElf32_Word0x00 0x00 0x00 0x1fThe name is defined by the string at index 31 of the string table: .text
sh_typeElf32_Word0x00 0x00 0x00 0x01The 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_flagsElf32_Word0x00 0x00 0x00 0x06This 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_addrElf32_Addr0x00 0x00 0x00 0x00A 0 seems to indicate that the section will not appear in the memory image of a process
sh_offsetElf32_Off0x00 0x00 0x00 0x34This is the byte offset from the beginning of the file to the first byte in the section
sh_sizeElf32_Word0x00 0x00 0x00 0x30Section size in bytes
sh_linkElf32_Word0x00 0x00 0x00 0x00Contains 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_infoElf32_Word0x00 0x00 0x00 0x00Contains extra information whose interpretation depends on the section type. It does not seem relevant though for an sh_type of SHT_PROGBITS
sh_addralignElf32_Word0x00 0x00 0x00 0x04Some sections have address alignment constraints. In this case, I’m not yet 100% certain what this value of 4 actually represents
sh_entsizeElf32_Word0x00 0x00 0x00 0x00The 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  |............%...|
NameLengthValueInterpretation
sh_nameElf32_Word0x00 0x00 0x00 0x1bThe name is defined by the string at index 27 of the string table: .rel.text
sh_typeElf32_Word0x00 0x00 0x00 0x09SHT_REL This section holds relocation entries without explicit addends. But, I’m not quite sure what this means, yet…
sh_flagsElf32_Word0x00 0x00 0x00 0x40SHF_INFO_LINK indicates that the sh_info field of this section header holds a section header table index
sh_addrElf32_Addr0x00 0x00 0x00 0x00A 0 seems to indicate that the section will not appear in the memory image of a process
sh_offsetElf32_Off0x00 0x00 0x01 0x2CThis is the byte offset from the beginning of the file to the first byte in the section
sh_sizeElf32_Word0x00 0x00 0x00 0x08Section size in bytes
sh_linkElf32_Word0x00 0x00 0x00 0x06This should represent the section header index of the associated symbol table. As defined by figure 4-12 in the spec
sh_infoElf32_Word0x00 0x00 0x00 0x01The section header index of the section to which the relocation applies. As defined by figure 4-12 in the spec
sh_addralignElf32_Word0x00 0x00 0x00 0x04Some sections have address alignment constraints. In this case, I’m not yet 100% certain what this value of 4 actually represents
sh_entsizeElf32_Word0x00 0x00 0x00 0x08This 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  |....+...........|
NameLengthValueInterpretation
sh_nameElf32_Word0x00 0x00 0x00 0x25The name is defined by the string at index 37 of the string table: .data
sh_typeElf32_Word0x00 0x00 0x00 0x01SHT_PROGBITS A section holding information defined by the program. Format and meaning are determined solely by the program
sh_flagsElf32_Word0x00 0x00 0x00 0x03This 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_addrElf32_Addr0x00 0x00 0x00 0x00A 0 seems to indicate that the section will not appear in the memory image of a process
sh_offsetElf32_Off0x00 0x00 0x00 0x64This is the byte offset from the beginning of the file to the first byte in the section
sh_sizeElf32_Word0x00 0x00 0x00 0x00Section size in bytes
sh_linkElf32_Word0x00 0x00 0x00 0x00Contains 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_infoElf32_Word0x00 0x00 0x00 0x00Contains 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_addralignElf32_Word0x00 0x00 0x00 0x01A value of 1 indicates that there are no alignment constraints
sh_entsizeElf32_Word0x00 0x00 0x00 0x00The 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...|
NameLengthValueInterpretation
sh_nameElf32_Word0x00 0x00 0x00 0x2bThe name is defined by the string at index 43 of the string table: .bss
sh_typeElf32_Word0x00 0x00 0x00 0x08SHT_NOBITS A section that occupies no space in the file.
sh_flagsElf32_Word0x00 0x00 0x00 0x03This 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_addrElf32_Addr0x00 0x00 0x00 0x00A 0 seems to indicate that the section will not appear in the memory image of a process
sh_offsetElf32_Off0x00 0x00 0x00 0x64Since this is a section of type SHT_NOBITS, this field contains the conceptual file offset
sh_sizeElf32_Word0x00 0x00 0x00 0x00Section size in bytes
sh_linkElf32_Word0x00 0x00 0x00 0x00Contains 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_infoElf32_Word0x00 0x00 0x00 0x00Contains 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_addralignElf32_Word0x00 0x00 0x00 0x01A value of 1 indicates that there are no alignment constraints
sh_entsizeElf32_Word0x00 0x00 0x00 0x00The 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  |................|
NameLengthValueInterpretation
sh_nameElf32_Word0x00 0x00 0x00 0x30The name is defined by the string at index 48 of the string table: .ARM.attributes
sh_typeElf32_Word0x70 0x00 0x00 0x03This value is in between SHT_LOPROC (0x70000000) and SHT_HIPROC (0x7FFFFFFF), which are values reserved for processor-specific semantics
sh_flagsElf32_Word0x00 0x00 0x00 0x00No flags
sh_addrElf32_Addr0x00 0x00 0x00 0x00A 0 seems to indicate that the section will not appear in the memory image of a process
sh_offsetElf32_Off0x00 0x00 0x00 0x64This is the byte offset from the beginning of the file to the first byte in the section
sh_sizeElf32_Word0x00 0x00 0x00 0x12Section size in bytes
sh_linkElf32_Word0x00 0x00 0x00 0x00Contains a section header table index link, interpretation depends on the section type. It does not seem relevant for this header type
sh_infoElf32_Word0x00 0x00 0x00 0x00Contains a section header table index link, interpretation depends on the section type. It does not seem relevant for this header type
sh_addralignElf32_Word0x00 0x00 0x00 0x01A value of 1 indicates that there are no alignment constraints
sh_entsizeElf32_Word0x00 0x00 0x00 0x00The 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  |................|
NameLengthValueInterpretation
sh_nameElf32_Word0x00 0x00 0x00 0x01The name is defined by the string at index 1 of the string table: .symtab
sh_typeElf32_Word0x00 0x00 0x00 0x02SHT_SYMTAB Section that holds a symbol table
sh_flagsElf32_Word0x00 0x00 0x00 0x00No flags
sh_addrElf32_Addr0x00 0x00 0x00 0x00A 0 seems to indicate that the section will not appear in the memory image of a process
sh_offsetElf32_Off0x00 0x00 0x00 0x78This is the byte offset from the beginning of the file to the first byte in the section
sh_sizeElf32_Word0x00 0x00 0x00 0xa0Section size in bytes
sh_linkElf32_Word0x00 0x00 0x00 0x07The section header index of the associated string table
sh_infoElf32_Word0x00 0x00 0x00 0x09One greater than the symbol table index of the last local symbol (binding STB_LOCAL)
sh_addralignElf32_Word0x00 0x00 0x00 0x04Some sections have address alignment constraints. In this case, I’m not yet 100% certain what this value of 4 actually represents
sh_entsizeElf32_Word0x00 0x00 0x00 0x10This 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  |................|
NameLengthValueInterpretation
sh_nameElf32_Word0x00 0x00 0x00 0x09The name is defined by the string at index 9 of the string table: .strtab
sh_typeElf32_Word0x00 0x00 0x00 0x03SHT_STRTAB This section holds a stringtable
sh_flagsElf32_Word0x00 0x00 0x00 0x00No flags
sh_addrElf32_Addr0x00 0x00 0x00 0x00A 0 seems to indicate that the section will not appear in the memory image of a process
sh_offsetElf32_Off0x00 0x00 0x01 0x18This is the byte offset from the beginning of the file to the first byte in the section
sh_sizeElf32_Word0x00 0x00 0x00 0x12Section size in bytes
sh_linkElf32_Word0x00 0x00 0x00 0x00Contains a section header table index link, interpretation depends on the section type. It does not seem relevant for this header type
sh_infoElf32_Word0x00 0x00 0x00 0x00Contains a section header table index link, interpretation depends on the section type. It does not seem relevant for this header type
sh_addralignElf32_Word0x00 0x00 0x00 0x01A value of 1 indicates that there are no alignment constraints
sh_entsizeElf32_Word0x00 0x00 0x00 0x00The 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
0002461006D7367002464
10005f737461727400

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\0msg\0$d
10\0_start\0

Resulting in the following string table

IndexString
0none
1$a
4msg
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:

NameLengthValueInterpretation
sh_nameElf32_Word0x00 0x00 0x00 0x11The name is defined by the string at index 17 of the string table: .shstrtab
sh_typeElf32_Word0x00 0x00 0x00 0x03SHT_STRTAB So the section will be holding a string table
sh_flagsElf32_Word0x00 0x00 0x00 0x00No flags
sh_addrElf32_Addr0x00 0x00 0x00 0x00A 0 seems to indicate that the section will not appear in the memory image of a process
sh_offsetElf32_Off0x00 0x00 0x01 0x34This is the byte offset from the beginning of the file to the first byte in the section
sh_sizeElf32_Word0x00 0x00 0x00 0x40Section size in bytes
sh_linkElf32_Word0x00 0x00 0x00 0x00Contains 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_infoElf32_Word0x00 0x00 0x00 0x00Contains extra information whose interpretation depends on the section type. It does not seem relevant though for an sh_type of SHT_STRTAB
sh_addralignElf32_Word0x00 0x00 0x00 0x01A value of 1 indicates that there are no alignment constraints
sh_entsizeElf32_Word0x00 0x00 0x00 0x00The 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
0002E73796D746162002E
10737472746162002E7368
20737472746162002E7265
306C2E74657874002E6461
407461002E627373002E41
50524D2E61747472696275
6074657300

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.symtab\0.
10strtab\0.sh
20strtab\0.re
30l.text\0.da
40ta\0.bss\0.A
50RM.attribu
60tes\0

And in order to have an easy and quick look-up table:

IndexString
0none
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