≡ Menu

Linux ELF Object File Format (and ELF Header Structure) Basics

ELF stands for executable and linkable file format.

ELF is used as standard file format for object files on Linux. Prior to this, the a.out file format was being used as a standard but lately ELF took over the charge as a standard.

ELF supports :

  • Different processors
  • Different data encoding
  • Different classes of machines

This article explains about the different types of ELF Object files and ELF header.

ELF Object Files

A file that contains compiled code is known as an object file. An Object file can be any of the following types :

1. Relocatable file

This type of object file contains data and code that can be linked together with other relocatable files to produce an executable binary or a shared object file. In a layman’s term, a relocatable file is same as the .o file produced when we compile a code the following way :

 gcc -Wall -c test.c -o test.o

So the test.o produced after the operation above would be a relocatable file.

2. Shared object file

This type of object file is used by the dynamic linker to combine it with the executable and/or other shared object files to create a complete process image. In a layman’s term, a shared object file is same as the .so file produced when the code is compiled with the -fPIC flag in the following way :

gcc -c -Wall -Werror -fPIC shared.c
gcc -shared -o libshared.so shared.o

After the above two commands are done, a shared object file libshared.o is produced as output.

NOTE: To know more about Linux shared libraries, refer to our article Linux shared libraries

3. Executable file

This type of object file is a file that is capable of executing a program when run. In a layman’s term, it is output of commands like this:

 gcc -Wall test.c -o test

So, the output ‘test’ would be an executable which when run would execute the logic written in test.c file.

So, as can be concluded from the above types of object files, an object file participates from program building to its execution or we can say from linking to execution stage (to know more about Linux/GCC compilation stages, refer to our article Journey of a C program).

ELF Header

All the object files that are described above are of ELF type on Linux.

This can easily be proved by looking into each of these files. For example, I looked into each of the three files in my system :

Relocatable file:

 $ vim func.o

^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^A^@>^@^A^@^@^@^@^@^@^@^@^@^@^@
^@UHå¿^@^@^@^@è^@^@^@^@¸^@^@^@^@è^@^@^@^@ÉÃ^@^@
 Inside func()^@^@GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3^@^T^@^@^@^@
^@^@^@^AzR^@^Ax^P^A^[^L^G^H^A^@^@^\^@^.symtab^@.strtab^@.shstrtab^
@.rela.text^@.data^@.bss^@.rodata^@.comment^@.note.GNU-stack^@.rela.eh_frame
^@^@^@^@^@^@^@^@^@^@^@^@^@

Shared object file:

$ vim libshared.so

^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^C^@>^@^A^@^@^@ ^@^@^@^@^@^A^@^@^@^F^@^
^@^@^@^@^@è^A^@^@^@^@^@^@è^A^@^@^@^@^@^@^A^@^@^@^@^@^@^@^D^@^@^@^T^@^@^
@^C^@^@^@GNU^@·YG®z^L^ZÊ7uÈí,?^N^@^@^@^@^C^@^@^@^L^@^@^@

Executable file:

$ vim test

^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^B^@>^@^A^@^@^@P@^@^@^@^@^@@^@^@^@^@^@<
^@^@^@D^@^@^^B^@^@^@^@^@^@^A^@^@^@^@^@^@^@/lib64/ld-linux-x86-64.so.2^@^D^
@^@^@^D^@^@^@^T^@^@^@^C^@^@^@GNU^@òÁ}CKbE;ära`6"^O^N\^C^@^@^@

So we see that as these are binary files so nothing much is comprehensible except the ELF string in the beginning of each file. This shows that these files are of ELF format only.

Each file begins with an ELF header that pretty much tells the complete organization of the file. For example, Relocatable and shared object files contain sections but on the other end the executable file is composed of segments. So depending upon the type of object file, the ELF header gives detailed information about the file.

Mostly in case of executable files, an ELF header is followed by program header table. A program header table helps in creating process image. Since it helps in creating process image (which is created after running the executable) so, program header table becomes mandatory for executable files but is optional for relocatable and shared object files.

The following is the organization of ELF header :

#define EI_NIDENT 16
typedef struct {
e_ident[EI_NIDENT];
unsigned char e_type;
Elf32_Half e_machine;
Elf32_Half e_version;
Elf32_Word e_entry;
Elf32_Addr e_phoff;
Elf32_Off e_shoff;
Elf32_Off e_flags;
Elf32_Word e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;

So we see that the organization shown above is in the form of a structure. Explaining each member in detail here would make things complex so lets go through the basic meaning and information held by each member of this structure to get an idea of that particular field.

1. e_ident

As we already know that ELF format supports various classes of machines, processors etc. So, in order to support all this the initial information in ELF file contains information on how to interpret the file independent of the processor on which the executable is running. The array ‘e_ident’ provides exactly the same information :

Name     Value      Purpose
EI_MAG     0          File identification
EI_MAG1    1          File identification
EI_MAG2    2          File identification
EI_MAG3    3          File identification
EI_CLASS   4          File class
EI_DATA    5          Data encoding
EI_VERSION 6          File version
EI_PAD     7          Start of padding bytes
EI_NIDENT  16         Size of e_ident[]
  • EI_MAG The first four bytes above hold the magic number ‘0x7fELF’.
  • EI_CLASS An ELF can have two classes, 32 bit or 64 bit. This makes the file format portable.
  • EI_DATA This member gives the information on data encoding. Simply put, this information tells whether the data is in big endian or little endian format.
  • EI_VERSION This member provides information on object file version.
  • EI_PAD This member marks the start of unused bytes in the e_indent array of information.
  • EI_NIDENT This member provides the size of array e_indent. This helps in parsing the ELF file.

2. e_type

This member identifies the type of object file. For example, an object file can be of following types :

Name    Value    Meaning
ET_NONE  0       No file type
ET_REL   1       Relocatable file
ET_EXEC  2       Executable file
ET_DYN   3       Shared object file
ET_CORE  4       Core file

NOTE: The above list is not exhaustive but still gives information on main object file types that ELF can refer to.

3. e_machine

This member gives information on architecture that an ELF file requires.

Name            Value      Meaning
ET_NONE           0          No machine
EM_M32            1          AT&T WE 32100
EM_SPARC          2          SPARC
EM_386            3          Intel Architecture
EM_68K            4          Motorola 68000
EM_88K            5          Motorola 88000
EM_860            7          Intel 80860
EM_MIPS           8          MIPS RS3000 Big-Endian
EM_MIPS_RS4_BE   10          MIPS RS4000 Big-Endian
RESERVED       11-16         Reserved for future use

4. Additional Members

Apart from the above three members, it also has the following members:

  • e_version: This member provides the ELF object file version information.
  • e_entry: This member provides the virtual address information of the entry point to which the system must transfer the control so that the process can be initiated.
  • e_phoff: This member holds the offset to program header table. This information is stored in terms of bytes. In absence of a program header table, the information contained by this member is zero.
  • e_shoff: This member holds the offset to section header table. As with e_phoff, this information too is stored in form of bytes and in absence of a section header table, the information contained by this field is zero.
  • e_flags: This member holds information related to process specific flags.
  • e_ehsize: This member holds information related to ELF header size in byes.
  • e_phentsize: This member holds information related to size of one entry in the object file’s program header table. Note that all the entries are same in size.
  • e_phnum: This member holds the information related to number of entries in program header table.
  • e_shentsize: This member holds the information related to size of one entry in the section header table. The size is represented in form of number of bytes.
  • e_shnum: This member gives the information related to the number of entries in the section header table.

Note that the product of ephnum and ephentsize gives the total size of program header table in bytes and same way the product of eshnum and eshentsize gives the total size of section header table in bytes.

Add your comment

If you enjoyed this article, you might also like..

  1. 50 Linux Sysadmin Tutorials
  2. 50 Most Frequently Used Linux Commands (With Examples)
  3. Top 25 Best Linux Performance Monitoring and Debugging Tools
  4. Mommy, I found it! – 15 Practical Linux Find Command Examples
  5. Linux 101 Hacks 2nd Edition eBook Linux 101 Hacks Book

Bash 101 Hacks Book Sed and Awk 101 Hacks Book Nagios Core 3 Book Vim 101 Hacks Book

Comments on this entry are closed.

  • bob July 2, 2012, 8:57 am

    One of the best articles I have read. I have been looking for something like this for years!!!

  • Jalal Hajigholamali July 2, 2012, 10:37 am

    Hi,

    Thanks, it was very useful for me

  • Frank July 2, 2012, 10:54 pm

    Hi,

    There is an error in the ELF header definition,

    typedef struct {
    e_ident[EI_NIDENT];
    unsigned char e_type;

    You missed the type of member e_ident[EI_NINDENT], it should be ‘unsigned char’.
    And the type of member e_type is ‘Elf32_Half’, NOT ‘unsigned char’

  • Lakshmanan July 3, 2012, 9:57 am

    Good one and clearly explained. 🙂

  • mariam ogunlana July 31, 2012, 11:35 am

    Dis is we’ll xpainatory nd interesting

  • Gibi September 25, 2012, 3:35 am

    Awesome …
    “In a layman’s term,” , good

  • prasad July 15, 2014, 2:54 am

    awesome article…………..Thanx

  • Mate Soos June 2, 2015, 3:48 am

    Hey, nice writeup! Note that you have a typo:

    “After the above two commands are done, a shared object file libshared.o is produced as output.”

    You meant “libshared.so” not “libshared.o” I guess 🙂

    Mate