segments(runtime) & sections(link time)
A segment points to an absolute address in the memory, and specify how many bytes are contained in that segment. The section works in a similar way, and if and the memory segment and section pointing to can have overlaps.
The segment also specifies where the memory should be loaded into virtual or physical memory. Segment tells OS how to map parts of ELF file into memory.
Executable -> at least has two segments: data segment(initialized globals and other initialized data, and usually use space more than it required for uninitialized data), and code segment(executable code will be loaded into memory).
Convert executable file to txt file in hex:
gcc hello.c -o hello
hexdump -C hello > output.txt
# or in linux
readelf -h hello
# in mac
greadelf -a hello
First 16 bytes is corresponding to e_ident[EI_NIDENT]. The first 4 bytes represent a magic number, which is same for all elf file. The next byte specify the class of ELF : 0 = None(invalid), 1 = 32bits Object, 2 = 64bits Object. The 6th byte indicates is the least significant byte or most significant byte come first. The following bytes are for Version, OS ABI, ABI version and zero padding.
#define EI_NIDENT 16
// Elf32_Half -> uint16_t
// Elf32_Word -> uint32_t
// Elf32_Addr -> uint32_t
// Elf64_Addr -> uint64_t
// Elf32_Off -> uint32_t
// Elf64_Off -> uint64_t
typedef struct
{
/*
program header table (pointed by e_phoff)
header 1
header 2
header 3 (number of entry -> e_phnum, size of entry -> e_ehsize)
*/
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ // 16 bytes to describe how ELF to be parsed
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture 0xF3 for risc-v */
Elf32_Word e_version; /* Object file version : always 1 */
Elf32_Addr e_entry; /* Entry point virtual address : entry point for executables or constructor for shared_library */
// All program headers are in an array directly behind each other in the ELF file
Elf32_Off e_phoff; /* Program header table file offset : location of program header table */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index : used to resolve the names of sections contained in the file*/
} Elf32_Ehdr;
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf64_Half e_type; /* Object file type */
Elf64_Half e_machine; /* Architecture */
Elf64_Word e_version; /* Object file version */
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
Elf64_Word e_flags; /* Processor-specific flags */
Elf64_Half e_ehsize; /* ELF header size in bytes */
Elf64_Half e_phentsize; /* Program header table entry size */
Elf64_Half e_phnum; /* Program header table entry count */
Elf64_Half e_shentsize; /* Section header table entry size */
Elf64_Half e_shnum; /* Section header table entry count */
Elf64_Half e_shstrndx; /* Section header string table index */
} Elf64_Ehdr;
e_type : type of ELF file
Relocatable file -> compile only mode : gcc -c
ELF with ET_EXEC :
it will not support ASLR(Address space layout randomization), as the EXEC type does not support position independent executable, and a modern compiler will disable PIE explicitly
program header
https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-83432/index.html
Each segment is defined by a small program header structure.
typedef struct elf32_phdr {
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
} Elf32_Phdr;
// p_offset: wherein the elf file of the content of segment is located
// p_vaddr: where is the first byte of the segment in memory
// p_paddr: used in context where physical addr is relevant: firmware file
// p_filesz: the size of the segment in the file, if it is 0, the segment is defined exclusively by the program header
// p_memsz: the size of the segment in memory if it is larger than the size in the file
// p_flags: permission of the segment, readable, writable or executable
// p_align: alignment requirement of the segment
typedef struct elf64_phdr {
Elf64_Word p_type;
Elf64_Word p_flags;
Elf64_Off p_offset; /* Segment file offset */
Elf64_Addr p_vaddr; /* Segment virtual address */
Elf64_Addr p_paddr; /* Segment physical address */
Elf64_Xword p_filesz; /* Segment size in file */
Elf64_Xword p_memsz; /* Segment size in memory */
Elf64_Xword p_align; /* Segment alignment, file & memory */
} Elf64_Phdr;
p_type : segment type
/* These constants are for the segment types stored in the image headers */
/* for p_type*/
#define PT_NULL 0 // place holder & also a simple way to disable the segment
#define PT_LOAD 1 // segment of this type will be loaded into memory, also can used to create zero initialize segments
#define PT_DYNAMIC 2 // load shared library related to executables
#define PT_INTERP 3 // position independent executable and shared library have an ELF type of dynamic,
// this macro helps to differenciate between both
#define PT_NOTE 4 // segment that holds this has auxiliary information for debugger
#define PT_SHLIB 5 // shared_lib but never used
#define PT_PHDR 6 // this segment specifies where program header table can be loaded
#define PT_TLS 7 /* Thread local storage segment */
#define PT_LOOS 0x60000000 /* OS-specific */ // e.g. which part of memory should be marked as readonly
#define PT_HIOS 0x6fffffff /* OS-specific */
#define PT_LOPROC 0x70000000
#define PT_HIPROC 0x7fffffff
#define PT_GNU_EH_FRAME 0x6474e550
#define PT_GNU_STACK (PT_LOOS + 0x474e551)
Explore the segment
# in Linux
readelf --segment hello
# in mac
greadelf --segment hello
section
sections also have section headers. Sections are mainly used when linking or by the debugger. It is ok to remove sections from ELF when you just want to run a program. (sstrip)
typedef struct elf32_shdr {
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;
typedef struct elf64_shdr {
Elf64_Word sh_name; /* Section name, index in string tbl (e_shstrndx in ELF32_Ehdr), point to a string table containing the name of the section*/
Elf64_Word sh_type; /* Type of section */
Elf64_Xword sh_flags; /* Miscellaneous section attributes */
Elf64_Addr sh_addr; /* Section virtual addr at execution */
Elf64_Off sh_offset; /* Section file offset */
Elf64_Xword sh_size; /* Size of section in bytes */
Elf64_Word sh_link; /* Index of another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;
sh_type
/* sh_type */
#define SHT_NULL 0
#define SHT_PROGBITS 1 // progbits section contains data for the program, code or variable
#define SHT_SYMTAB 2 // symbol table -> local or global symbol
#define SHT_STRTAB 3 // string table section
#define SHT_RELA 4 // relocation section type : how to modify different sections when assembling an executable
#define SHT_HASH 5
#define SHT_DYNAMIC 6
#define SHT_NOTE 7
#define SHT_NOBITS 8 // uninitialized data
#define SHT_REL 9
#define SHT_SHLIB 10
#define SHT_DYNSYM 11
#define SHT_NUM 12
#define SHT_LOPROC 0x70000000
#define SHT_HIPROC 0x7fffffff
#define SHT_LOUSER 0x80000000
#define SHT_HIUSER 0xffffffff
sh_flags
/* sh_flags */
#define SHF_WRITE 0x1 // section is writable
#define SHF_ALLOC 0x2
#define SHF_EXECINSTR 0x4
#define SHF_MASKPROC 0xf0000000
Explore the section
# in Linux
readelf --sections hello
# in mac
greadelf --sections hello