#include "load.h"
#include "object_info.h"
#include "elf.h"
#include "error.h"
#include "util.h"
#include "xmalloc.h"
#include <string.h>
#include <sys/mman.h>
#include <errno.h>
static int get_prot(int flags)
{
int prot;
prot = 0;
if (flags & PF_X)
prot |= PROT_EXEC;
if (flags & PF_W)
prot |= PROT_WRITE;
if (flags & PF_R)
prot |= PROT_READ;
if (!prot)
prot = PROT_NONE;
return prot;
}
static void get_segments(Elf32_Phdr *program_header_table, int program_header_count, void *base_address, struct segment_info_struct **segment_table, int *segment_count)
{
int i;
Elf32_Phdr *program_header;
off_t page_offset;
Elf32_Addr vaddr;
size_t mem_size;
*segment_table = xmalloc(program_header_count * sizeof (struct segment_info_struct));
*segment_count = 0;
for (i = 0; i < program_header_count; i++)
if ((program_header = &program_header_table[i])->p_type == PT_LOAD) {
page_offset = PAGE_OFFSET(program_header->p_vaddr);
vaddr = program_header->p_vaddr - page_offset;
mem_size = program_header->p_memsz + page_offset;
(*segment_table)[*segment_count].load_address = base_address + vaddr;
(*segment_table)[*segment_count].size = mem_size;
(*segment_table)[*segment_count].prot = get_prot(program_header->p_flags);
(*segment_count)++;
}
}
static void examine_bottom_half(Elf32_Phdr *program_header_table, int program_header_count, const struct program_data_struct *program_data, void *base_address, void *entry_point, const char *invocation_name, struct loaded_object_info_struct *loaded_object_info)
{
struct segment_info_struct *segment_table;
int segment_count;
get_segments(program_header_table, program_header_count, base_address, &segment_table, &segment_count);
loaded_object_info->base_address = base_address;
loaded_object_info->load_address = base_address + PAGE(program_data->load_vaddr);
loaded_object_info->segment_table = segment_table;
loaded_object_info->segment_count = segment_count;
loaded_object_info->entry_point = entry_point;
loaded_object_info->dynamic_array = program_data->contents.dynamic_array ? base_address + program_data->dynamic_array_vaddr : NULL;
loaded_object_info->interpreter_name = program_data->contents.interpreter_name ? base_address + program_data->interpreter_name_vaddr : NULL;
}
void examine_executable(Elf32_Phdr *program_header_table, int program_header_count, void *entry_point, const char *invocation_name, struct loaded_object_info_struct *loaded_object_info)
{
struct program_data_struct program_data;
void *base_address;
read_program_header_table(program_header_table, program_header_count, &program_data, invocation_name);
base_address = program_data.contents.program_header_table ? (void *)program_header_table - program_data.program_header_table_vaddr : NULL;
examine_bottom_half(program_header_table, program_header_count, &program_data, base_address, entry_point, invocation_name, loaded_object_info);
}
void examine_dynamic_linker(Elf32_Phdr *program_header_table, int program_header_count, void *base_address, void *entry_point, struct loaded_object_info_struct *loaded_object_info)
{
const char *invocation_name = "(dynamic linker)";
struct program_data_struct program_data;
read_program_header_table(program_header_table, program_header_count, &program_data, invocation_name);
examine_bottom_half(program_header_table, program_header_count, &program_data, base_address, entry_point, invocation_name, loaded_object_info);
}
void examine_vdso(void *load_address, struct loaded_object_info_struct *loaded_object_info)
{
Elf32_Ehdr *elf_header;
Elf32_Phdr *program_header_table;
struct program_data_struct program_data;
void *base_address;
struct segment_info_struct *segment_table;
int segment_count;
elf_header = load_address;
program_header_table = load_address + elf_header->e_phoff;
read_program_header_table(program_header_table, elf_header->e_phnum, &program_data, "(vdso)");
base_address = load_address - PAGE(program_data.load_vaddr);
get_segments(program_header_table, elf_header->e_phnum, base_address, &segment_table, &segment_count);
loaded_object_info->base_address = base_address;
loaded_object_info->load_address = load_address;
loaded_object_info->segment_table = segment_table;
loaded_object_info->segment_count = segment_count;
loaded_object_info->entry_point = elf_header->e_entry ? base_address + elf_header->e_entry : NULL;
loaded_object_info->dynamic_array = program_data.contents.dynamic_array ? base_address + program_data.dynamic_array_vaddr : NULL;
loaded_object_info->interpreter_name = program_data.contents.interpreter_name ? base_address + program_data.interpreter_name_vaddr : NULL;
}
void load_object_file(const char *filename, int flags, struct loaded_object_info_struct *loaded_object_info)
{
FILE *fp;
int fd;
Elf32_Ehdr elf_header;
void *base_address;
Elf32_Phdr *program_header_table, *program_header;
struct program_data_struct program_data;
struct segment_info_struct *segment_table;
int segment_count;
int i;
off_t page_offset;
off_t offset;
Elf32_Addr vaddr;
size_t file_size, mem_size, total_mem_size;
int prot;
size_t map_file_size, map_mem_size;
void *load_addr;
if (!(fp = fopen(filename, "r")))
file_error_errno(filename, "cannot open object file");
fd = fileno(fp);
fread_elf_header(&elf_header, fp, filename);
switch (elf_header.e_type) {
case ET_EXEC:
if (flags & LF_NOEXEC)
file_error(filename, "cannot dynamically load executable");
base_address = NULL;
break;
case ET_DYN:
base_address = MAP_FAILED;
break;
default:
file_error(filename, "object file cannot be loaded");
}
if (elf_header.e_phentsize != sizeof (Elf32_Phdr))
file_error(filename, "cannot read program header table");
program_header_table = xmalloc(elf_header.e_phnum * sizeof (Elf32_Phdr));
if (fseek(fp, elf_header.e_phoff, SEEK_SET) == -1)
file_error_errno("seek error", filename);
fread_program_header_table(program_header_table, elf_header.e_phnum, fp, filename);
read_program_header_table(program_header_table, elf_header.e_phnum, &program_data, filename);
segment_table= xmalloc(elf_header.e_phnum * sizeof (struct segment_info_struct));
segment_count = 0;
for (i = 0; i < elf_header.e_phnum; i++)
if ((program_header = &program_header_table[i])->p_type == PT_LOAD) {
page_offset = PAGE_OFFSET(program_header->p_vaddr);
offset = program_header->p_offset - page_offset;
vaddr = program_header->p_vaddr - page_offset;
file_size = program_header->p_filesz + page_offset;
mem_size = program_header->p_memsz + page_offset;
prot = get_prot(program_header->p_flags);
map_file_size = PAGE_ALIGN(file_size);
map_mem_size = min(map_file_size, mem_size);
if (base_address == MAP_FAILED) {
total_mem_size = program_data.mem_size + page_offset;
if (total_mem_size == 0)
file_error(filename, "cannot load object file");
if ((load_addr = mmap(NULL, total_mem_size, prot, MAP_PRIVATE, fd, offset)) == MAP_FAILED)
file_error_errno(filename, "failed to map segment from object file");
base_address = load_addr - vaddr;
if (PAGE_ALIGN(map_mem_size) < total_mem_size)
mprotect(load_addr + PAGE_ALIGN(map_mem_size), total_mem_size - PAGE_ALIGN(map_mem_size), PROT_NONE);
}
else {
if (map_mem_size > 0)
if (mmap(base_address + vaddr, map_mem_size, prot, MAP_PRIVATE | MAP_FIXED, fd, offset) == MAP_FAILED)
file_error_errno(filename, "failed to map segment from object file");
load_addr = base_address + vaddr;
}
if (file_size < map_mem_size)
memset(load_addr + file_size, 0, map_mem_size - file_size);
if (mem_size > map_file_size)
if (mmap(load_addr + map_file_size, mem_size - map_file_size, prot, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == MAP_FAILED)
file_error_errno(filename, "failed to map zero-fill pages");
segment_table[segment_count].load_address = load_addr;
segment_table[segment_count].size = mem_size;
segment_table[segment_count].prot = prot;
segment_count++;
}
free(program_header_table);
fclose(fp);
loaded_object_info->base_address = base_address;
loaded_object_info->load_address = base_address + PAGE(program_data.load_vaddr);
loaded_object_info->segment_table = segment_table;
loaded_object_info->segment_count = segment_count;
loaded_object_info->entry_point = elf_header.e_entry ? base_address + elf_header.e_entry : NULL;
loaded_object_info->dynamic_array = program_data.contents.dynamic_array ? base_address + program_data.dynamic_array_vaddr : NULL;
loaded_object_info->interpreter_name = program_data.contents.interpreter_name ? base_address + program_data.interpreter_name_vaddr : NULL;
}
void make_segments_writable(struct object_info_struct *object_info)
{
int i;
if (!object_info->segment_table)
return;
for (i = 0; i < object_info->segment_count; i++)
if (!(object_info->segment_table[i].prot & PROT_WRITE))
if (mprotect(object_info->segment_table[i].load_address, object_info->segment_table[i].size, PROT_READ | PROT_WRITE) == -1)
fatal_sys_error(errno, "failed to make segment writable for relocation");
}
void restore_segments_prot(struct object_info_struct *object_info)
{
int i;
if (!object_info->segment_table)
return;
for (i = 0; i < object_info->segment_count; i++)
if (!(object_info->segment_table[i].prot & PROT_WRITE))
if (mprotect(object_info->segment_table[i].load_address, object_info->segment_table[i].size, object_info->segment_table[i].prot) == -1)
fatal_sys_error(errno, "failed to restore segment protection");
}