header
#define EI_NIDENT 16
typedef struct elf64_hdr {
// 魔数占用 16字节
unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */
// Elf64_Half e_type;
__u16 e_type;
// Elf64_Half e_machine;
__u16 e_machine;
// Elf64_Word e_version;
__u32 e_version;
// Elf64_Addr e_entry;
__u64 e_entry; /* Entry point virtual address */
// Elf64_Off e_phoff;
__u64 e_phoff; /* Program header table file offset */
// Elf64_Off e_shoff;
__u64 e_shoff; /* Section header table file offset */
// Elf64_Word e_flags;
__u32 e_flags;
// Elf64_Half e_ehsize;
__u32 e_ehsize;
// Elf64_Half e_phentsize;
__u16 e_phentsize;
// Elf64_Half e_phnum;
__u16 e_phnum;
// Elf64_Half e_shentsize;
__u16 e_shentsize;
// Elf64_Half e_shnum;
__u16 e_shnum;
// Elf64_Half e_shstrndx;
__u16 e_shstrndx;
} Elf64_Ehdr;
合计头部占用 66 字节
typedef struct elf64_phdr {
// Elf64_Word p_type;
__u32 p_type;
// Elf64_Word p_flags;
__u32 p_flags;
// Elf64_Off p_offset; /* Segment file offset */
__u64 p_offset; /* Segment file offset */
// Elf64_Addr p_vaddr; /* Segment virtual address */
__u64 p_vaddr; /* Segment virtual address */
// Elf64_Addr p_paddr; /* Segment physical address */
__u64 p_paddr; /* Segment physical address */
// Elf64_Xword p_filesz; /* Segment size in file */
__u64 p_filesz; /* Segment size in file */
// Elf64_Xword p_memsz; /* Segment size in memory */
__u64 p_memsz; /* Segment size in memory */
// Elf64_Xword p_align; /* Segment alignment, file & memory */
__u64 p_align; /* Segment alignment, file & memory */
} Elf64_Phdr;
size = 4 + 4 + 8 + 8 + 8 + 8 + 8 + 8= 56 字节
header 信息
大小 |
名称 |
含义 |
值 |
16 |
e_ident |
elf文件的魔数 |
\177ELF |
2 |
e_type |
类型必须是 ET_EXEC 或者 ET_DYN |
ET_EXEC |
2 |
e_machine |
指令集架构,例如 x86_64 |
EM_X86_64 |
4 |
e_version |
未知 |
|
4 |
e_phoff |
header 占用的偏移量,或者说header占用的字节数 |
|
2 |
e_phentsize |
elf_phdr 结构体的大小,起到版本校验的作用 |
|
2 |
e_phnum |
elf_phdr 结构的个数 |
|
PT_INTERP 段信息 (interpreter)
大小 |
名称 |
含义 |
值 |
=== |
=== |
===== 段描述信息 PT_INTERP 开始 ======= |
=== |
4 |
p_type |
类型 |
PT_INTERP |
8 |
p_offset |
interpreter 文件路径在 elf 文件中的偏移量 |
|
8 |
p_filesz |
interpreter 文件路径的大小 |
|
大小 |
名称 |
含义 |
值 |
4 |
p_flags |
PF_X 表示是否可执行 |
|
大小 |
名称 |
含义 |
值 |
4 |
p_flags |
是否可读,可写,可执行 |
PF_X , PF_W , PF_X |
8 |
p_vaddr |
段数据的虚拟地址 |
|
大小 |
名称 |
含义 |
值 |
4 |
p_type |
类型 |
|
8 |
p_offset |
对应段文件在文件中的偏移量 |
|
8 |
p_filesz |
对应段文件在文件中的大小 |
|
=== |
=== |
===== 段文件地址信息 1开始 ======= |
=== |
p_filesz |
elf_interpreter |
段文件地址和名称 |
|
=== |
=== |
===== 段文件地址信息 2开始 ======= |
=== |
p_filesz |
elf_interpreter |
段文件地址和名称 |
|
#define elfhdr elf64_hdr
#define ELFMAG "\177ELF"
#define SELFMAG 4
#define EM_X86_64 62 /* AMD x86-64 architecture */
#define PT_INTERP 3
#define PATH_MAX 4096
#define PF_R 0x4
#define PF_W 0x2
#define PF_X 0x1
#define PT_LOOS 0x60000000
#define PT_LOPROC 0x70000000
#define PT_HIPROC 0x7fffffff
#define PT_GNU_STACK (PT_LOOS + 0x474e551)
/* Stack area protections */
#define EXSTACK_DEFAULT 0 /* Whatever the arch defaults to */
#define EXSTACK_DISABLE_X 1 /* Disable executable stacks */
#define EXSTACK_ENABLE_X 2 /* Enable executable stacks */
static int load_elf_binary(struct linux_binprm *bprm) {
struct file *interpreter = NULL;
struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
unsigned long elf_bss, elf_brk;
char * elf_interpreter = NULL;
int executable_stack = EXSTACK_DEFAULT;
unsigned long start_code, end_code, start_data, end_data;
struct {
struct elfhdr elf_ex;
struct elfhdr interp_elf_ex;
} *loc;
// 比较 elf 文件 header 信息中的魔数是否是 \177ELF,
if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
goto out;
// #define ET_EXEC 2
// #define ET_DYN 3
// type 必须是 2 或者 3
if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN)
goto out;
// 检查 loc->elf_ex.e_machine ==
// if (!elf_check_arch(&loc->elf_ex))
if ((&loc->elf_ex)->e_machine != EM_X86_64 )
goto out;
elf_phdata = load_elf_phdrs(&loc->elf_ex, bprm->file);
elf_ppnt = elf_phdata;
elf_bss = 0;
elf_brk = 0;
start_code = ~0UL;
end_code = 0;
start_data = 0;
end_data = 0;
// e_phnum 就是 elf_phdr 数组的个数
// ================ 这一大段逻辑只为了处理 PT_INTERP 类型的段信息 =======
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
*/
// p_fileez 是 segmentSize
if (elf_ppnt->p_filesz > PATH_MAX ||
elf_ppnt->p_filesz < 2)
goto out_free_ph;
// 临时数据
elf_interpreter = kmalloc(elf_ppnt->p_filesz, GFP_KERNEL);
// 数据的偏移
pos = elf_ppnt->p_offset;
retval = kernel_read(bprm->file, elf_interpreter, elf_ppnt->p_filesz, &pos);
// interpreter 是一个文件
interpreter = open_exec(elf_interpreter);
pos = 0;
// 加载 interpreter 的 header
retval = kernel_read(interpreter, &loc->interp_elf_ex, sizeof(loc->interp_elf_ex), &pos);
break;
}
elf_ppnt++;
}
// =========================================================
// =========== 由于 arch_elf_pt_proc 在 x86架构下什么也不做, ========
// =========== 所以主要作用是获取 PT_GNU_STACK 段的信息 ==============
// 设为数组首部,重新开始遍历
elf_ppnt = elf_phdata;
// e_phnum 是段描述的个数
for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
switch (elf_ppnt->p_type) {
case PT_GNU_STACK:
if (elf_ppnt->p_flags & PF_X)
executable_stack = EXSTACK_ENABLE_X;
else
executable_stack = EXSTACK_DISABLE_X;
break;
case PT_LOPROC ... PT_HIPROC:
retval = arch_elf_pt_proc(&loc->elf_ex, elf_ppnt,
bprm->file, false,
&arch_state);
break;
}
}
// ============================================================
// ======================= interpreter 文件的校验 ===============
if (elf_interpreter) {
// 校验 elf 文件的魔数
if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
goto out_free_dentry;
// 校验指令集架构是不是 x86_64
if (!elf_check_arch(&loc->interp_elf_ex) ||
elf_check_fdpic(&loc->interp_elf_ex))
goto out_free_dentry;
// 加载 interpreter 文件的段描述信息部分
interp_elf_phdata = load_elf_phdrs(&loc->interp_elf_ex, interpreter);
// 遇到给 elf_ppnt 赋值,就说明要开始遍历所有的段描述信息了
elf_ppnt = interp_elf_phdata;
for (i = 0; i < loc->interp_elf_ex.e_phnum; i++, elf_ppnt++) {
switch (elf_ppnt->p_type) {
case PT_LOPROC ... PT_HIPROC:
// 目前什么也不做
retval = arch_elf_pt_proc(&loc->interp_elf_ex,
elf_ppnt, interpreter, true, &arch_state);
break;
}
}
}
// ============================================================
// 目前 x86_64 什么也不会做
retval = arch_check_elf(&loc->elf_ex, !!interpreter, &loc->interp_elf_ex, &arch_state);
// 刷掉旧的信息
retval = flush_old_exec(bprm);
// 设置 personality
SET_PERSONALITY2(loc->elf_ex, &arch_state);
if (elf_read_implies_exec(loc->elf_ex, executable_stack)) {
// 修改进程的 personality
current->personality |= READ_IMPLIES_EXEC;
}
// 随机的虚拟空间地址
if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
current->flags |= PF_RANDOMIZE;
// 设置新的信息
setup_new_exec(bprm);
install_exec_creds(bprm);
// TODO:
retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP), executable_stack);
// 栈的起始地址
current->mm->start_stack = bprm->p;
// ===================== 处理 PT_LOAD 类型的段信息 ==================
// 再一次遍历段信息
unsigned long load_addr = 0, load_bias = 0;
unsigned long elf_bss, elf_brk;
elf_bss = 0;
elf_brk = 0;
start_code = ~0UL;
end_code = 0;
start_data = 0;
end_data = 0;
for(i = 0, elf_ppnt = elf_phdata; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
int elf_prot = 0, elf_flags, elf_fixed = MAP_FIXED_NOREPLACE;
unsigned long k, vaddr;
unsigned long total_size = 0;
// 表明只处理 PT_LOAD 类型的段
if (elf_ppnt->p_type != PT_LOAD)
continue;
if (elf_brk > elf_bss) {
// brk 是在内存中最后一个段的结束位置,bss 是在 elf 文件中的最后一个段的结束位置
unsigned long nbyte;
}
if (elf_ppnt->p_flags & PF_R) {
// 该段对应的信息可读
elf_prot |= PROT_READ;
}
if (elf_ppnt->p_flags & PF_W) {
// 该段对应的信息可写
elf_prot |= PROT_WRITE;
}
if (elf_ppnt->p_flags & PF_X) {
// 该段对应的信息可执行
elf_prot |= PROT_EXEC;
}
// 默认情况下,一个段的信息是私有的,禁止写的,可执行的
elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;
vaddr = elf_ppnt->p_vaddr;
if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
// header 的类型是 ET_EXEC,可执行程序
elf_flags |= elf_fixed;
} else if (loc->elf_ex.e_type == ET_DYN) {
// ET_DYN 表示共享库
// TODO: 暂时先跳过吧
}
// 将段信息映射到内存中
error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
elf_prot, elf_flags, total_size);
// 如果 load_addr_set == 0
if (!load_addr_set) {
load_addr_set = 1;
// 段数据的虚拟地址 - 段数据在文件中偏移
load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
if (loc->elf_ex.e_type == ET_DYN) {
// 共享库
load_bias += error -
ELF_PAGESTART(load_bias + vaddr);
load_addr += load_bias;
reloc_func_desc = load_bias;
}
}
k = elf_ppnt->p_vaddr;
if (k < start_code)
start_code = k; // 说明 start_code 是所有段数据里最靠前的那个
if (start_data < k)
start_data = k; // 说明 start_data 是所有段数据里最靠后的那个
// 现在 k 是段数据的末尾位置
k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;
if (k > elf_bss)
elf_bss = k; // 说明 elf_bss 也是非常靠后的
if ((elf_ppnt->p_flags & PF_X) && end_code < k) // 当前段是可执行的,且段数据的结束位置更靠后则满足
end_code = k; // 也就是说 end_code 指向最靠后的那个可执行段的结束
if (end_data < k)
end_data = k; // data 就是最后一个段信息的结束
// 虚拟地址 + 段数据在内存中的大小
k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
if (k > elf_brk) {
bss_prot = elf_prot; // bss 的权限由在内存中最后一个段的权限控制
elf_brk = k; // brk 指向在内存中最后一个段的结束位置
}
}
// e_entry 是入口指令或者说 main 函数的位置
loc->elf_ex.e_entry += load_bias;
elf_bss += load_bias;
elf_brk += load_bias;
start_code += load_bias;
end_code += load_bias;
start_data += load_bias;
end_data += load_bias;
}
static struct elf_phdr *load_elf_phdrs(struct elfhdr *elf_ex,
struct file *elf_file) {
struct elf_phdr *elf_phdata = NULL;
int retval, size, err = -1;
// Segment file offset
loff_t pos = elf_ex->e_phoff;
// phdata 大小 == e_phentsize,也就是 56 字节
if (elf_ex->e_phentsize != sizeof(struct elf_phdr))
goto out;
// phnum 范围检查
if (elf_ex->e_phnum < 1 ||
elf_ex->e_phnum > 65536U / sizeof(struct elf_phdr))
goto out;
// 也就是说 e_phnum 是 phdata 的个数
size = sizeof(struct elf_phdr) * elf_ex->e_phnum;
// ELF_MIN_ALIGN == 1 << 12 = 4096 字节 = 4kb
// 也就是 phdata 的大小需要满足 一个 page 的大小
if (size > ELF_MIN_ALIGN)
goto out;
// 分配空间,由此可知 elf_phdata 是一个 elf_phdr 数组
elf_phdata = kmalloc(size, GFP_KERNEL);
retval = kernel_read(elf_file, elf_phdata, size, &pos);
return elf_phdata;
}
ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos) {
ssize_t result;
result = vfs_read(file, (void __user *)buf, count, pos);
return result;
}
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) {
ssize_t ret;
ret = __vfs_read(file, buf, count, pos);
return ret;
}
ssize_t __vfs_read(struct file *file, char __user *buf, size_t count,
loff_t *pos) {
if (file->f_op->read)
return file->f_op->read(file, buf, count, pos);
}
static inline int arch_elf_pt_proc(struct elfhdr *ehdr,
struct elf_phdr *phdr,
struct file *elf, bool is_interp,
struct arch_elf_state *state) {
return 0;
}
static inline int arch_check_elf(struct elfhdr *ehdr, bool has_interp,
struct elfhdr *interp_ehdr,
struct arch_elf_state *state) {
return 0;
}
随机栈顶
// 最大偏移量
#define
STACK_RND_MASK 0x3fffff
static unsigned long randomize_stack_top(unsigned long stack_top) {
unsigned long random_variable = 0;
// 当前配置了随机栈顶
if (current->flags & PF_RANDOMIZE) {
// 随机long值
random_variable = get_random_long();
// 相当于取模
random_variable &= STACK_RND_MASK;
// 栈顶的单位是 字节,而随机值随的是页数
// PAGE_SHIFT 是一页的偏移,1页默认是 4KB
random_variable <<= PAGE_SHIFT;
}
// 默认栈是自上而下的
return PAGE_ALIGN(stack_top) - random_variable;
}
static unsigned long elf_map(struct file *filep, // elf 文件
unsigned long addr, // 段数据的起始地址
struct elf_phdr *eppnt, // 段描述信息
int prot, // 访问权限
int type, // 映射属性
unsigned long total_size) {
unsigned long map_addr;
// 段数据在文件中的大小 + 段数据的起始虚拟地址
unsigned long size = eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr);
// 段数据在文件中的偏移 - 段的起始虚拟地址
unsigned long off = eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr);
// 页对齐
addr = ELF_PAGESTART(addr);
size = ELF_PAGEALIGN(size);
if (!size)
return addr;
if (total_size) {
} else {
map_addr = vm_mmap(filep, addr, size, prot, type, off);
}
// 实际映射的起始位置
return(map_addr);
}