Linux ELF Loading
From Ggl's wiki
Contents |
Loading
Static
- All PT_LOAD segements are mapped into memory at the correct locations (p_vaddr)
- Needed libraries are also loaded
- Needed relocations are performed
- Control is transfered to the main executable entry point by setting the pc (program counter) to the Elf header's entry point (e_entry)
- The stack is initialized
Dynamic
- The dynamic linker (usually /lib/ld-linux.so.2) is mapped into memory
- If PT_INTERP exists the pc is set to the entry point of the program interpreter
- The stack is initialized
Stack state
| position | content | size (bytes) + comment |
|---|---|---|
| stack pointer -> | [ argc = number of args ] | 4 |
| [ argv[0] (pointer) ] | 4 (program name) | |
| [ argv[1] (pointer) ] | 4 | |
| [ argv[..] (pointer) ] | 4 * x | |
| [ argv[n - 1] (pointer) ] | 4 | |
| [ argv[n] (pointer) ] | 4 (= NULL) | |
| [ envp[0] (pointer) ] | 4 | |
| [ envp[1] (pointer) ] | 4 | |
| [ envp[..] (pointer) ] | 4 | |
| [ envp[term] (pointer) ] | 4 (= NULL) | |
| [ auxv[0] (Elf32_auxv_t) ] | 8 | |
| [ auxv[1] (Elf32_auxv_t) ] | 8 | |
| [ auxv[..] (Elf32_auxv_t) ] | 8 | |
| [ auxv[term] (Elf32_auxv_t) ] | 8 (= AT_NULL vector) | |
| [ padding ] | 0 - 16 | |
| [ argument ASCIIZ strings ] | >= 0 | |
| [ environment ASCIIZ str. ] | >= 0 | |
| (0xbffffffc) | [ end marker ] | 4 (= NULL) |
| (0xc0000000) | < top of stack > | 0 (virtual) |
Details
(from /usr/src/linux-`uname -r`/fs/binfmt_elf.c
Useful usual kernel functions to know:
kmalloc kfree kernel_read set_brk
The main kernel function which loads an elf binary is load_elf_binary.
It works mainly on this structure :
struct {
struct elfhdr elf_ex;
struct elfhdr interp_elf_ex;
struct exec interp_ex;
} *loc;
It begins by getting the elf header of the binary file :
/* Get the exec-header */ loc->elf_ex = *((struct elfhdr *) bprm->buf);
and after do some consistency checks :
/* First of all, some simple consistency checks */ if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0) goto out; if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN) goto out; if (!elf_check_arch(&loc->elf_ex)) goto out; if (!bprm->file->f_op||!bprm->file->f_op->mmap) goto out;
So, it checks the magic bytes (e_ident), the type of elf file (ET_EXEC is for an executable file, ET_DYN is a shared object file), the arch.
It continues by getting Program Header info :
/* Now read in all of the header information */ retval = -ENOMEM; if (loc->elf_ex.e_phentsize != sizeof(struct elf_phdr)) goto out; if (loc->elf_ex.e_phnum > 65536U / sizeof(struct elf_phdr)) goto out; size = loc->elf_ex.e_phnum * sizeof(struct elf_phdr); elf_phdata = (struct elf_phdr *) kmalloc(size, GFP_KERNEL);
and read program header into elf_phdata :
retval = kernel_read(bprm->file, loc->elf_ex.e_phoff, (char *) elf_phdata, size);
Now to parse the program it sets a pointer to it :
elf_ppnt = elf_phdata;
The phdr is parsed with this loop to find the PT_INTERP entry :
for (i = 0; i < loc->elf_ex.e_phnum; i++) {
if (elf_ppnt->p_type == PT_INTERP) {
/* This is the program interpreter used for
* shared libraries - for now assume that this
* is an a.out format binary
*/
[...]
/* Get the exec headers */
loc->interp_ex = *((struct exec *) bprm->buf);
loc->interp_elf_ex = *((struct elfhdr *) bprm->buf);
break;
}
elf_ppnt++;
}
Checks if it has PT_GNU_STACK :
elf_ppnt = elf_phdata;
for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
if (elf_ppnt->p_type == PT_GNU_STACK) {
if (elf_ppnt->p_flags & PF_X)
executable_stack = EXSTACK_ENABLE_X;
else
executable_stack = EXSTACK_DISABLE_X;
break;
}
have_pt_gnu_stack = (i < loc->elf_ex.e_phnum);
Useful C structures (32 bits)
(from /usr/include/elf.h)
The ELF file header
(This appears at the start of every ELF file)
#define EI_NIDENT (16)
typedef struct { unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ Elf32_Half e_type; /* Object file type */ Elf32_Half e_machine; /* Architecture */ Elf32_Word e_version; /* Object file version */ Elf32_Addr e_entry; /* Entry point virtual address */ Elf32_Off e_phoff; /* Program header table file offset */ 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 */ } Elf32_Ehdr;
/* Fields in the e_ident array. The EI_* macros are indices into the array. The macros under each EI_* macro are the values the byte may have. */
#define EI_MAG0 0 /* File identification byte 0 index */ #define ELFMAG0 0x7f /* Magic number byte 0 */ #define EI_MAG1 1 /* File identification byte 1 index */ #define ELFMAG1 'E' /* Magic number byte 1 */ #define EI_MAG2 2 /* File identification byte 2 index */ #define ELFMAG2 'L' /* Magic number byte 2 */ #define EI_MAG3 3 /* File identification byte 3 index */ #define ELFMAG3 'F' /* Magic number byte 3 */
/* Conglomeration of the identification bytes, for easy testing as a word. */
#define ELFMAG "\177ELF" #define SELFMAG 4 #define EI_CLASS 4 /* File class byte index */ #define ELFCLASSNONE 0 /* Invalid class */ #define ELFCLASS32 1 /* 32-bit objects */ #define ELFCLASS64 2 /* 64-bit objects */ #define ELFCLASSNUM 3 #define EI_DATA 5 /* Data encoding byte index */ #define ELFDATANONE 0 /* Invalid data encoding */ #define ELFDATA2LSB 1 /* 2's complement, little endian */ #define ELFDATA2MSB 2 /* 2's complement, big endian */ #define ELFDATANUM 3 #define EI_VERSION 6 /* File version byte index */ /* Value must be EV_CURRENT */ #define EI_OSABI 7 /* OS ABI identification */ #define ELFOSABI_NONE 0 /* UNIX System V ABI */ #define ELFOSABI_SYSV 0 /* Alias. */ #define ELFOSABI_NETBSD 2 /* NetBSD. */ #define ELFOSABI_LINUX 3 /* Linux. */ #define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ #define ELFOSABI_FREEBSD 9 /* FreeBSD. */ #define ELFOSABI_OPENBSD 12 /* OpenBSD. */ #define ELFOSABI_ARM 97 /* ARM */ #define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ #define EI_ABIVERSION 8 /* ABI version */ #define EI_PAD 9 /* Byte index of padding bytes */
/* Legal values for e_type (object file type). */
#define ET_NONE 0 /* No file type */ #define ET_REL 1 /* Relocatable file */ #define ET_EXEC 2 /* Executable file */ #define ET_DYN 3 /* Shared object file */ #define ET_CORE 4 /* Core file */ #define ET_NUM 5 /* Number of defined types */ #define ET_LOOS 0xfe00 /* OS-specific range start */ #define ET_HIOS 0xfeff /* OS-specific range end */ #define ET_LOPROC 0xff00 /* Processor-specific range start */ #define ET_HIPROC 0xffff /* Processor-specific range end */
/* Legal values for e_machine (architecture). */
#define EM_NONE 0 /* No machine */ #define EM_M32 1 /* AT&T WE 32100 */ #define EM_SPARC 2 /* SUN SPARC */ #define EM_386 3 /* Intel 80386 */ #define EM_68K 4 /* Motorola m68k family */ #define EM_88K 5 /* Motorola m88k family */ #define EM_860 7 /* Intel 80860 */ #define EM_MIPS 8 /* MIPS R3000 big-endian */ #define EM_S370 9 /* IBM System/370 */ #define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ #define EM_PPC 20 /* PowerPC */ #define EM_PPC64 21 /* PowerPC 64-bit */ #define EM_ARM 40 /* ARM */ #define EM_FAKE_ALPHA 41 /* Digital Alpha */ #define EM_SH 42 /* Hitachi SH */ #define EM_COLDFIRE 52 /* Motorola Coldfire */ #define EM_68HC12 53 /* Motorola M68HC12 */
Section Header
typedef struct
{
Elf32_Word sh_name; /* Section name (string tbl index) */
Elf32_Word sh_type; /* Section type */
Elf32_Word sh_flags; /* Section flags */
Elf32_Addr sh_addr; /* Section virtual addr at execution */
Elf32_Off sh_offset; /* Section file offset */
Elf32_Word sh_size; /* Section size in bytes */
Elf32_Word sh_link; /* Link to another section */
Elf32_Word sh_info; /* Additional section information */
Elf32_Word sh_addralign; /* Section alignment */
Elf32_Word sh_entsize; /* Entry size if section holds table */
} Elf32_Shdr;
Symbol table entry
typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf32_Section st_shndx; /* Section index */
} Elf32_Sym;
Relocation table entry
(without addend (in section of type SHT_REL)
typedef struct
{
Elf32_Addr r_offset; /* Address */
Elf32_Word r_info; /* Relocation type and symbol index */
} Elf32_Rel;
Program segment header
typedef struct
{
Elf32_Word p_type; /* Segment type */
Elf32_Off p_offset; /* Segment file offset */
Elf32_Addr p_vaddr; /* Segment virtual address */
Elf32_Addr p_paddr; /* Segment physical address */
Elf32_Word p_filesz; /* Segment size in file */
Elf32_Word p_memsz; /* Segment size in memory */
Elf32_Word p_flags; /* Segment flags */
Elf32_Word p_align; /* Segment alignment */
} Elf32_Phdr;
/* Legal values for p_type (segment type). */
#define PT_NULL 0 /* Program header table entry unused */ #define PT_LOAD 1 /* Loadable program segment */ #define PT_DYNAMIC 2 /* Dynamic linking information */ #define PT_INTERP 3 /* Program interpreter */ #define PT_NOTE 4 /* Auxiliary information */ #define PT_SHLIB 5 /* Reserved */ #define PT_PHDR 6 /* Entry for header table itself */ #define PT_TLS 7 /* Thread-local storage segment */ #define PT_NUM 8 /* Number of defined types */ #define PT_LOOS 0x60000000 /* Start of OS-specific */ #define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ #define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ #define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ #define PT_LOSUNW 0x6ffffffa #define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ #define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ #define PT_HISUNW 0x6fffffff #define PT_HIOS 0x6fffffff /* End of OS-specific */ #define PT_LOPROC 0x70000000 /* Start of processor-specific */ #define PT_HIPROC 0x7fffffff /* End of processor-specific */
/* Legal values for p_flags (segment flags). */
#define PF_X (1 << 0) /* Segment is executable */ #define PF_W (1 << 1) /* Segment is writable */ #define PF_R (1 << 2) /* Segment is readable */ #define PF_MASKOS 0x0ff00000 /* OS-specific */ #define PF_MASKPROC 0xf0000000 /* Processor-specific */
typedef struct
{
Elf32_Sword d_tag; /* Dynamic entry type */
union
{
Elf32_Word d_val; /* Integer value */
Elf32_Addr d_ptr; /* Address value */
} d_un;
} Elf32_Dyn;
Auxiliary vector
/* This vector is normally only used by the program interpreter. The usual definition in an ABI supplement uses the name auxv_t. The vector is not usually defined in a standard <elf.h> file, but it can't hurt. We rename it to avoid conflicts. The sizes of these types are an arrangement between the exec server and the program interpreter, so we don't fully specify them here. */
typedef struct
{
int a_type; /* Entry type */
union
{
long int a_val; /* Integer value */
void *a_ptr; /* Pointer value */
void (*a_fcn) (void); /* Function pointer value */
} a_un;
} Elf32_auxv_t;
/* Legal values for a_type (entry type). */
#define AT_NULL 0 /* End of vector */ #define AT_IGNORE 1 /* Entry should be ignored */ #define AT_EXECFD 2 /* File descriptor of program */ #define AT_PHDR 3 /* Program headers for program */ #define AT_PHENT 4 /* Size of program header entry */ #define AT_PHNUM 5 /* Number of program headers */ #define AT_PAGESZ 6 /* System page size */ #define AT_BASE 7 /* Base address of interpreter */ #define AT_FLAGS 8 /* Flags */ #define AT_ENTRY 9 /* Entry point of program */ #define AT_NOTELF 10 /* Program is not ELF */ #define AT_UID 11 /* Real uid */ #define AT_EUID 12 /* Effective uid */ #define AT_GID 13 /* Real gid */ #define AT_EGID 14 /* Effective gid */ #define AT_CLKTCK 17 /* Frequency of times() */

