#include "jit-internal.h"
#include "jit-rules.h"
#include "jit-elf-defs.h"
#ifdef JIT_WIN32_PLATFORM
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#else
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) && defined(HAVE_MPROTECT)
#define JIT_USE_MMAP_TO_LOAD 1
#ifndef MAP_ANON
#ifdef MAP_ANONYMOUS
#define MAP_ANON MAP_ANONYMOUS
#else
#define MAP_ANON 0
#endif
#endif
#ifndef MAP_FIXED
#define MAP_FIXED 0
#endif
#ifndef MAP_COPY
#define MAP_COPY MAP_PRIVATE
#endif
#endif
#endif
#endif
#include <stdio.h>
#ifdef JIT_NATIVE_INT32
typedef Elf32_Ehdr Elf_Ehdr;
typedef Elf32_Shdr Elf_Shdr;
typedef Elf32_Phdr Elf_Phdr;
typedef Elf32_Addr Elf_Addr;
typedef Elf32_Word Elf_Word;
typedef Elf32_Xword Elf_Xword;
typedef Elf32_Off Elf_Off;
typedef Elf32_Dyn Elf_Dyn;
typedef Elf32_Sym Elf_Sym;
typedef Elf32_Rel Elf_Rel;
typedef Elf32_Rela Elf_Rela;
#define ELF_R_SYM(val) ELF32_R_SYM((val))
#define ELF_R_TYPE(val) ELF32_R_TYPE((val))
#else
typedef Elf64_Ehdr Elf_Ehdr;
typedef Elf64_Shdr Elf_Shdr;
typedef Elf64_Phdr Elf_Phdr;
typedef Elf64_Addr Elf_Addr;
typedef Elf64_Word Elf_Word;
typedef Elf64_Xword Elf_Xword;
typedef Elf64_Off Elf_Off;
typedef Elf64_Dyn Elf_Dyn;
typedef Elf64_Sym Elf_Sym;
typedef Elf64_Rel Elf_Rel;
typedef Elf64_Rela Elf_Rela;
#define ELF_R_SYM(val) ELF64_R_SYM((val))
#define ELF_R_TYPE(val) ELF64_R_TYPE((val))
#endif
#ifdef JIT_WIN32_NATIVE
#define sys_open _open
#define sys_close _close
#define sys_read _read
#define sys_lseek _lseek
#else
#define sys_open open
#define sys_close close
#define sys_read read
#define sys_lseek lseek
#endif
#ifndef O_BINARY
#define O_BINARY 0
#endif
typedef int (*jit_reloc_func)(jit_readelf_t readelf, void *address,
int type, jit_nuint value, int has_addend,
jit_nuint addend);
static jit_reloc_func get_reloc(unsigned int machine);
struct jit_readelf
{
jit_readelf_t next;
int resolved;
Elf_Ehdr ehdr;
unsigned char *phdrs;
unsigned char *shdrs;
char *regular_strings;
jit_nuint regular_strings_size;
char *dynamic_strings;
jit_nuint dynamic_strings_size;
Elf_Sym *symbol_table;
jit_nuint symbol_table_size;
Elf_Word *symbol_hash;
jit_nuint symbol_hash_size;
Elf_Word symbol_hash_buckets;
jit_reloc_func reloc_func;
void *map_address;
jit_nuint map_size;
int free_with_munmap;
};
#define JIT_ELF_IS_MALLOCED 0x01000000
static Elf_Phdr *get_phdr(jit_readelf_t readelf, unsigned int index)
{
if(index < readelf->ehdr.e_phnum &&
readelf->ehdr.e_phentsize >= sizeof(Elf_Phdr))
{
return (Elf_Phdr *)
(readelf->phdrs +
index * ((unsigned int)(readelf->ehdr.e_phentsize)));
}
else
{
return 0;
}
}
static Elf_Shdr *get_shdr(jit_readelf_t readelf, unsigned int index)
{
if(index < readelf->ehdr.e_shnum &&
readelf->ehdr.e_shentsize >= sizeof(Elf_Shdr))
{
return (Elf_Shdr *)
(readelf->shdrs +
index * ((unsigned int)(readelf->ehdr.e_shentsize)));
}
else
{
return 0;
}
}
static const char *get_string(jit_readelf_t readelf, Elf_Word _index)
{
jit_nuint index = (jit_nuint)_index;
if(index < readelf->regular_strings_size)
{
return readelf->regular_strings + index;
}
else
{
return 0;
}
}
static const char *get_dyn_string(jit_readelf_t readelf, Elf_Addr _index)
{
jit_nuint index = (jit_nuint)_index;
if(index < readelf->dynamic_strings_size)
{
return readelf->dynamic_strings + index;
}
else
{
return 0;
}
}
static int map_program(jit_readelf_t readelf, int fd)
{
Elf_Off file_size;
Elf_Off memory_size;
Elf_Off start, end;
Elf_Phdr *phdr;
unsigned int index;
void *base_address;
unsigned char *segment_address;
file_size = 0;
memory_size = 0;
for(index = 0; index < readelf->ehdr.e_phnum; ++index)
{
phdr = get_phdr(readelf, index);
if(!phdr)
{
continue;
}
start = phdr->p_offset;
end = start + phdr->p_filesz;
if(end > file_size)
{
file_size = end;
}
start = phdr->p_vaddr;
end = start + phdr->p_memsz;
if(end > memory_size)
{
memory_size = end;
}
}
if(memory_size < file_size)
{
memory_size = file_size;
}
base_address = 0;
#ifdef JIT_USE_MMAP_TO_LOAD
{
Elf_Off page_size;
Elf_Off rounded_file_size;
Elf_Off temp_start;
Elf_Off temp_end;
int zero_fd, prot;
page_size = (Elf_Off)(jit_vmem_page_size());
end = memory_size;
if((end % page_size) != 0)
{
end += page_size - (end % page_size);
}
rounded_file_size = file_size;
if((rounded_file_size % page_size) != 0)
{
rounded_file_size += page_size - (rounded_file_size % page_size);
}
zero_fd = sys_open("/dev/zero", O_RDWR, 0);
if(zero_fd < -1)
{
goto failed_mmap;
}
base_address = mmap(0, (size_t)end, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, zero_fd, 0);
close(zero_fd);
if(base_address == (void *)(jit_nint)(-1))
{
base_address = 0;
goto failed_mmap;
}
for(index = 0; index < readelf->ehdr.e_phnum; ++index)
{
phdr = get_phdr(readelf, index);
if(phdr)
{
temp_start = phdr->p_offset;
temp_end = temp_start + phdr->p_filesz;
temp_start -= (temp_start % page_size);
if((temp_end % page_size) != 0)
{
temp_end += page_size - (temp_end % page_size);
}
start = phdr->p_vaddr;
start -= (start % page_size);
if(temp_start < temp_end)
{
segment_address =
((unsigned char *)base_address) + (jit_nuint)start;
prot = 0;
if((phdr->p_flags & PF_X) != 0)
{
prot |= PROT_EXEC;
}
if((phdr->p_flags & PF_W) != 0)
{
prot |= PROT_WRITE;
}
if((phdr->p_flags & PF_R) != 0)
{
prot |= PROT_READ;
}
if(mmap(segment_address, (size_t)(temp_end - temp_start),
prot, MAP_COPY | MAP_FILE | MAP_FIXED, fd,
(off_t)temp_start) == (void *)(jit_nint)(-1))
{
munmap(base_address, (size_t)end);
base_address = 0;
goto failed_mmap;
}
}
}
}
readelf->free_with_munmap = 1;
for(index = 0; index < readelf->ehdr.e_phnum; ++index)
{
phdr = get_phdr(readelf, index);
if(phdr && phdr->p_filesz < phdr->p_memsz)
{
temp_start = phdr->p_vaddr + phdr->p_filesz;
start = (temp_start % page_size);
temp_start -= start;
if(start != 0)
{
segment_address =
((unsigned char *)base_address) +
(jit_nuint)temp_start;
mprotect(segment_address, (size_t)page_size,
PROT_READ | PROT_WRITE);
jit_memzero(segment_address + (jit_nuint)start,
(unsigned int)(page_size - start));
prot = 0;
if((phdr->p_flags & PF_X) != 0)
{
prot |= PROT_EXEC;
}
if((phdr->p_flags & PF_W) != 0)
{
prot |= PROT_WRITE;
}
if((phdr->p_flags & PF_R) != 0)
{
prot |= PROT_READ;
}
mprotect(segment_address, (size_t)page_size, prot);
}
}
}
}
failed_mmap:
#endif
if(!base_address)
{
base_address = _jit_malloc_exec(memory_size);
if(!base_address)
{
return 0;
}
for(index = 0; index < readelf->ehdr.e_phnum; ++index)
{
phdr = get_phdr(readelf, index);
if(phdr)
{
segment_address = ((unsigned char *)base_address) +
(jit_nuint)(phdr->p_vaddr);
if(lseek(fd, (off_t)(phdr->p_offset), 0) !=
(off_t)(phdr->p_offset) ||
read(fd, segment_address, (size_t)(phdr->p_filesz))
!= (int)(size_t)(phdr->p_filesz))
{
_jit_free_exec(base_address, memory_size);
return 0;
}
}
}
}
readelf->map_address = base_address;
readelf->map_size = memory_size;
return 1;
}
static void *map_section(int fd, Elf_Off offset, Elf_Xword file_size,
Elf_Xword memory_size, Elf_Word flags)
{
void *address;
if(memory_size < file_size)
{
memory_size = file_size;
}
address = _jit_malloc_exec(memory_size);
if(!address)
{
return 0;
}
if(lseek(fd, (off_t)offset, 0) != (off_t)offset)
{
_jit_free_exec(address, memory_size);
return 0;
}
if(read(fd, address, (size_t)file_size) != (int)(size_t)file_size)
{
_jit_free_exec(address, memory_size);
return 0;
}
return address;
}
static void unmap_section(void *address, Elf_Xword file_size,
Elf_Xword memory_size, Elf_Word flags)
{
if(memory_size < file_size)
{
memory_size = file_size;
}
if((flags & JIT_ELF_IS_MALLOCED) != 0)
{
_jit_free_exec(address, (unsigned int)memory_size);
}
}
typedef struct
{
Elf_Dyn *dyn;
jit_nuint size;
} jit_dynamic_iter_t;
static void dynamic_iter_init(jit_dynamic_iter_t *iter, jit_readelf_t readelf)
{
iter->dyn = jit_readelf_get_section_by_type
(readelf, SHT_DYNAMIC, &(iter->size));
}
static int dynamic_iter_next
(jit_dynamic_iter_t *iter, jit_uint *type, Elf_Addr *value)
{
if(iter->size >= sizeof(Elf_Dyn))
{
*type = (jit_uint)(iter->dyn->d_tag);
*value = iter->dyn->d_un.d_ptr;
if(*type == DT_NULL)
{
return 0;
}
++(iter->dyn);
iter->size -= sizeof(Elf_Dyn);
return 1;
}
else
{
return 0;
}
}
static int dynamic_for_type
(jit_readelf_t readelf, jit_uint type, Elf_Addr *value)
{
Elf_Addr temp_value;
jit_dynamic_iter_t iter;
jit_uint iter_type;
dynamic_iter_init(&iter, readelf);
while(dynamic_iter_next(&iter, &iter_type, &temp_value))
{
if(iter_type == type)
{
if(value)
{
*value = temp_value;
}
return 1;
}
}
return 0;
}
static void load_dynamic_section(jit_readelf_t readelf, int flags)
{
Elf_Addr value;
Elf_Addr value2;
jit_dynamic_iter_t iter;
jit_uint type;
jit_nuint size;
if(dynamic_for_type(readelf, DT_STRTAB, &value) &&
dynamic_for_type(readelf, DT_STRSZ, &value2))
{
readelf->dynamic_strings = jit_readelf_map_vaddr
(readelf, (jit_nuint)value);
if(readelf->dynamic_strings)
{
readelf->dynamic_strings_size = (jit_nuint)value2;
}
}
readelf->symbol_table = jit_readelf_get_section_by_type
(readelf, SHT_DYNSYM, &size);
if(readelf->symbol_table)
{
if(dynamic_for_type(readelf, DT_SYMENT, &value) &&
value == sizeof(Elf_Sym))
{
readelf->symbol_table_size = size / sizeof(Elf_Sym);
readelf->symbol_hash = jit_readelf_get_section_by_type
(readelf, SHT_HASH, &size);
if(readelf->symbol_hash)
{
readelf->symbol_hash_size = size / sizeof(Elf_Word);
if(readelf->symbol_hash_size >= 2)
{
readelf->symbol_hash_buckets = readelf->symbol_hash[0];
}
}
}
else
{
readelf->symbol_table = 0;
}
}
if((flags & JIT_READELF_FLAG_DEBUG) == 0)
{
return;
}
dynamic_iter_init(&iter, readelf);
while(dynamic_iter_next(&iter, &type, &value))
{
switch(type)
{
case DT_NEEDED:
{
printf("needed library: %s\n", get_dyn_string(readelf, value));
}
break;
case DT_PLTRELSZ:
{
printf("total size of PLT relocs: %ld\n", (long)value);
}
break;
case DT_PLTGOT:
{
printf("address of PLTGOT table: 0x%lx\n", (long)value);
}
break;
case DT_HASH:
{
printf("address of symbol hash table: 0x%lx\n", (long)value);
}
break;
case DT_STRTAB:
{
printf("address of string table: 0x%lx\n", (long)value);
}
break;
case DT_SYMTAB:
{
printf("address of symbol table: 0x%lx\n", (long)value);
}
break;
case DT_STRSZ:
{
printf("size of string table: %ld\n", (long)value);
}
break;
case DT_SYMENT:
{
printf("size of one symbol table entry: %ld\n", (long)value);
}
break;
case DT_INIT:
{
printf("address of init function: 0x%lx\n", (long)value);
}
break;
case DT_FINI:
{
printf("address of fini function: 0x%lx\n", (long)value);
}
break;
case DT_SONAME:
{
printf("library name: %s\n", get_dyn_string(readelf, value));
}
break;
case DT_REL:
{
printf("address of Rel relocs: 0x%lx\n", (long)value);
}
break;
case DT_RELSZ:
{
printf("total size of Rel relocs: %ld\n", (long)value);
}
break;
case DT_RELENT:
{
printf("size of one Rel reloc: %ld\n", (long)value);
}
break;
case DT_RELA:
{
printf("address of Rela relocs: 0x%lx\n", (long)value);
}
break;
case DT_RELASZ:
{
printf("total size of Rela relocs: %ld\n", (long)value);
}
break;
case DT_RELAENT:
{
printf("size of one Rela reloc: %ld\n", (long)value);
}
break;
case DT_PLTREL:
{
printf("type of PLT relocs: %ld\n", (long)value);
}
break;
case DT_JMPREL:
{
printf("address of PLT relocs: 0x%lx\n", (long)value);
}
break;
default:
{
printf("dynamic info of type 0x%x: 0x%lx\n",
(int)type, (long)value);
}
break;
}
}
for(size = 0; size < readelf->symbol_table_size; ++size)
{
printf("%08lX %02X%02X %2d %s\n",
(long)(readelf->symbol_table[size].st_value),
(int)(readelf->symbol_table[size].st_info),
(int)(readelf->symbol_table[size].st_other),
(int)(readelf->symbol_table[size].st_shndx),
get_dyn_string(readelf, readelf->symbol_table[size].st_name));
}
printf("number of symbols: %ld\n", (long)(readelf->symbol_table_size));
printf("number of symbol hash entries: %ld\n",
(long)(readelf->symbol_hash_size));
}
int jit_readelf_open(jit_readelf_t *_readelf, const char *filename, int flags)
{
int fd;
Elf_Ehdr ehdr;
Elf_Phdr *phdr;
Elf_Shdr *shdr;
jit_elf_info_t elf_info;
jit_readelf_t readelf;
unsigned int phdr_size;
unsigned int shdr_size;
unsigned int index;
void *address;
union
{
jit_ushort value;
unsigned char bytes[2];
} un;
_jit_gen_get_elf_info(&elf_info);
if((fd = sys_open(filename, O_RDONLY | O_BINARY, 0)) < 0)
{
return JIT_READELF_CANNOT_OPEN;
}
if(sys_read(fd, ehdr.e_ident, EI_NIDENT) != EI_NIDENT)
{
sys_close(fd);
return JIT_READELF_NOT_ELF;
}
if(ehdr.e_ident[EI_MAG0] != ELFMAG0 || ehdr.e_ident[EI_MAG1] != ELFMAG1 ||
ehdr.e_ident[EI_MAG2] != ELFMAG2 || ehdr.e_ident[EI_MAG3] != ELFMAG3)
{
sys_close(fd);
return JIT_READELF_NOT_ELF;
}
#ifdef JIT_NATIVE_INT32
if(ehdr.e_ident[EI_CLASS] != ELFCLASS32)
{
sys_close(fd);
return JIT_READELF_WRONG_ARCH;
}
#else
if(ehdr.e_ident[EI_CLASS] != ELFCLASS64)
{
sys_close(fd);
return JIT_READELF_WRONG_ARCH;
}
#endif
un.value = 0x0102;
if(un.bytes[0] == 0x01)
{
if(ehdr.e_ident[EI_DATA] != ELFDATA2MSB)
{
sys_close(fd);
return JIT_READELF_WRONG_ARCH;
}
}
else
{
if(ehdr.e_ident[EI_DATA] != ELFDATA2LSB)
{
sys_close(fd);
return JIT_READELF_WRONG_ARCH;
}
}
if(ehdr.e_ident[EI_VERSION] != EV_CURRENT)
{
sys_close(fd);
return JIT_READELF_BAD_FORMAT;
}
if(sys_read(fd, &(ehdr.e_type), sizeof(Elf_Ehdr) - EI_NIDENT)
!= (sizeof(Elf_Ehdr) - EI_NIDENT))
{
sys_close(fd);
return JIT_READELF_BAD_FORMAT;
}
if(ehdr.e_type != ET_DYN)
{
sys_close(fd);
return JIT_READELF_WRONG_ARCH;
}
if((flags & JIT_READELF_FLAG_FORCE) == 0)
{
if(ehdr.e_machine != elf_info.machine ||
ehdr.e_ident[EI_OSABI] != elf_info.abi ||
ehdr.e_ident[EI_ABIVERSION] != elf_info.abi_version)
{
sys_close(fd);
return JIT_READELF_WRONG_ARCH;
}
}
if(ehdr.e_version != EV_CURRENT)
{
sys_close(fd);
return JIT_READELF_BAD_FORMAT;
}
if(ehdr.e_ehsize < sizeof(ehdr))
{
sys_close(fd);
return JIT_READELF_BAD_FORMAT;
}
if((readelf = jit_cnew(struct jit_readelf)) == 0)
{
sys_close(fd);
return JIT_READELF_MEMORY;
}
readelf->ehdr = ehdr;
phdr_size = ((unsigned int)(ehdr.e_phnum)) *
((unsigned int)(ehdr.e_phentsize));
shdr_size = ((unsigned int)(ehdr.e_shnum)) *
((unsigned int)(ehdr.e_shentsize));
if(phdr_size > 0)
{
readelf->phdrs = (unsigned char *)jit_malloc(phdr_size);
if(!(readelf->phdrs))
{
jit_free(readelf);
sys_close(fd);
return JIT_READELF_MEMORY;
}
}
if(shdr_size > 0)
{
readelf->shdrs = (unsigned char *)jit_malloc(shdr_size);
if(!(readelf->shdrs))
{
jit_free(readelf->phdrs);
jit_free(readelf);
sys_close(fd);
return JIT_READELF_MEMORY;
}
}
if(phdr_size > 0)
{
if(lseek(fd, (off_t)(ehdr.e_phoff), 0) != (off_t)(ehdr.e_phoff) ||
read(fd, readelf->phdrs, phdr_size) != (int)phdr_size)
{
jit_free(readelf->shdrs);
jit_free(readelf->phdrs);
jit_free(readelf);
sys_close(fd);
return JIT_READELF_BAD_FORMAT;
}
}
if(shdr_size > 0)
{
if(lseek(fd, (off_t)(ehdr.e_shoff), 0) != (off_t)(ehdr.e_shoff) ||
read(fd, readelf->shdrs, shdr_size) != (int)shdr_size)
{
jit_free(readelf->shdrs);
jit_free(readelf->phdrs);
jit_free(readelf);
sys_close(fd);
return JIT_READELF_BAD_FORMAT;
}
}
if(!map_program(readelf, fd))
{
jit_readelf_close(readelf);
sys_close(fd);
return JIT_READELF_MEMORY;
}
if(shdr_size > 0)
{
for(index = 0; index < ehdr.e_shnum; ++index)
{
shdr = get_shdr(readelf, index);
if(!shdr)
{
continue;
}
if((shdr->sh_flags & SHF_ALLOC) != 0 || shdr->sh_addr != 0)
{
address = jit_readelf_map_vaddr(readelf, shdr->sh_addr);
if(address)
{
continue;
}
}
if(shdr->sh_size == 0)
{
continue;
}
address = map_section
(fd, shdr->sh_offset, shdr->sh_size, shdr->sh_size,
((shdr->sh_flags & SHF_WRITE) != 0 ? (PF_W | PF_R) : PF_R));
if(!address)
{
jit_readelf_close(readelf);
sys_close(fd);
return JIT_READELF_MEMORY;
}
shdr->sh_offset = (Elf_Off)(jit_nuint)address;
shdr->sh_flags |= JIT_ELF_IS_MALLOCED;
}
}
sys_close(fd);
shdr = get_shdr(readelf, ehdr.e_shstrndx);
if(shdr)
{
if((shdr->sh_flags & JIT_ELF_IS_MALLOCED) != 0)
{
readelf->regular_strings = (char *)(jit_nuint)(shdr->sh_offset);
}
else
{
readelf->regular_strings =
(char *)jit_readelf_map_vaddr(readelf, shdr->sh_addr);
}
if(readelf->regular_strings)
{
readelf->regular_strings_size = (jit_nuint)(shdr->sh_size);
}
}
if((flags & JIT_READELF_FLAG_DEBUG) != 0)
{
printf("header: machine=%d, abi=%d, abi_version=%d\n",
(int)(ehdr.e_machine), (int)(ehdr.e_ident[EI_OSABI]),
(int)(ehdr.e_ident[EI_ABIVERSION]));
for(index = 0; index < ehdr.e_phnum; ++index)
{
phdr = get_phdr(readelf, index);
if(phdr)
{
printf("program segment: type=%d, flags=0x%x, "
"vaddr=0x%lx, file_size=%ld, memory_size=%ld\n",
(int)(phdr->p_type),
(int)(phdr->p_flags & ~JIT_ELF_IS_MALLOCED),
(long)(phdr->p_vaddr),
(long)(phdr->p_filesz),
(long)(phdr->p_memsz));
}
}
for(index = 0; index < ehdr.e_shnum; ++index)
{
shdr = get_shdr(readelf, index);
if(shdr)
{
printf("section %2d: name=\"%s\", type=%d, flags=0x%x, "
"vaddr=0x%lx, size=%ld\n",
index,
get_string(readelf, shdr->sh_name),
(int)(shdr->sh_type),
(int)(shdr->sh_flags & ~JIT_ELF_IS_MALLOCED),
(long)(shdr->sh_addr),
(long)(shdr->sh_size));
}
}
}
readelf->reloc_func = get_reloc((unsigned int)(ehdr.e_machine));
load_dynamic_section(readelf, flags);
*_readelf = readelf;
return JIT_READELF_OK;
}
void jit_readelf_close(jit_readelf_t readelf)
{
unsigned int index;
Elf_Shdr *shdr;
if(!readelf)
{
return;
}
#ifdef JIT_USE_MMAP_TO_LOAD
if(readelf->free_with_munmap)
{
munmap(readelf->map_address, (size_t)(readelf->map_size));
}
else
#endif
{
_jit_free_exec(readelf->map_address, readelf->map_size);
}
for(index = 0; index < readelf->ehdr.e_shnum; ++index)
{
shdr = get_shdr(readelf, index);
if(shdr && (shdr->sh_flags & JIT_ELF_IS_MALLOCED) != 0)
{
unmap_section
((void *)(jit_nuint)(shdr->sh_offset),
shdr->sh_size, shdr->sh_size, shdr->sh_flags);
}
}
jit_free(readelf->phdrs);
jit_free(readelf->shdrs);
jit_free(readelf);
}
const char *jit_readelf_get_name(jit_readelf_t readelf)
{
Elf_Addr value;
if(dynamic_for_type(readelf, DT_SONAME, &value))
{
return get_dyn_string(readelf, value);
}
else
{
return 0;
}
}
void *jit_readelf_get_symbol(jit_readelf_t readelf, const char *name)
{
unsigned long hash;
unsigned long temp;
unsigned int index;
jit_nuint num_symbols;
Elf_Sym *symbol;
const char *symbol_name;
if(!readelf || !name || !(readelf->symbol_table))
{
return 0;
}
hash = 0;
index = 0;
while(name[index] != 0)
{
hash = (hash << 4) + (unsigned long)(name[index] & 0xFF);
temp = (hash & 0xF0000000);
if(temp != 0)
{
hash ^= temp | (temp >> 24);
}
++index;
}
if(readelf->symbol_hash_buckets != 0)
{
hash %= (unsigned long)(readelf->symbol_hash_buckets);
temp = (unsigned long)(readelf->symbol_hash[hash + 2]);
while(temp != 0 && temp < readelf->symbol_table_size)
{
symbol = &(readelf->symbol_table[temp]);
symbol_name = get_dyn_string(readelf, symbol->st_name);
if(symbol_name && !jit_strcmp(symbol_name, name))
{
if(symbol->st_shndx)
{
return jit_readelf_map_vaddr
(readelf, (jit_nuint)(symbol->st_value));
}
break;
}
temp = (unsigned long)(readelf->symbol_hash
[temp + readelf->symbol_hash_buckets + 2]);
}
return 0;
}
symbol = readelf->symbol_table;
for(num_symbols = readelf->symbol_table_size;
num_symbols > 0; --num_symbols)
{
symbol_name = get_dyn_string(readelf, symbol->st_name);
if(symbol_name && !jit_strcmp(symbol_name, name))
{
if(symbol->st_shndx)
{
return jit_readelf_map_vaddr
(readelf, (jit_nuint)(symbol->st_value));
}
}
++symbol;
}
return 0;
}
void *jit_readelf_get_section
(jit_readelf_t readelf, const char *name, jit_nuint *size)
{
unsigned int index;
Elf_Shdr *shdr;
const char *temp_name;
if(!readelf || !name)
{
return 0;
}
for(index = 0; index < readelf->ehdr.e_shnum; ++index)
{
shdr = get_shdr(readelf, index);
if(shdr)
{
temp_name = get_string(readelf, shdr->sh_name);
if(temp_name && !jit_strcmp(name, temp_name))
{
if(size)
{
*size = (jit_nuint)(shdr->sh_size);
}
if((shdr->sh_flags & JIT_ELF_IS_MALLOCED) != 0)
{
return (void *)(jit_nuint)(shdr->sh_offset);
}
else
{
return jit_readelf_map_vaddr
(readelf, (jit_nuint)(shdr->sh_addr));
}
}
}
}
return 0;
}
void *jit_readelf_get_section_by_type
(jit_readelf_t readelf, jit_int type, jit_nuint *size)
{
unsigned int index;
Elf_Shdr *shdr;
if(!readelf)
{
return 0;
}
for(index = 0; index < readelf->ehdr.e_shnum; ++index)
{
shdr = get_shdr(readelf, index);
if(shdr && type == (jit_int)(shdr->sh_type))
{
if(size)
{
*size = (jit_nuint)(shdr->sh_size);
}
if((shdr->sh_flags & JIT_ELF_IS_MALLOCED) != 0)
{
return (void *)(jit_nuint)(shdr->sh_offset);
}
else
{
return jit_readelf_map_vaddr
(readelf, (jit_nuint)(shdr->sh_addr));
}
}
}
return 0;
}
void *jit_readelf_map_vaddr(jit_readelf_t readelf, jit_nuint vaddr)
{
unsigned int index;
Elf_Phdr *phdr;
if(!readelf)
{
return 0;
}
for(index = 0; index < readelf->ehdr.e_phnum; ++index)
{
phdr = get_phdr(readelf, index);
if(phdr && vaddr >= phdr->p_vaddr &&
vaddr < (phdr->p_vaddr + phdr->p_memsz))
{
return (void *)(((unsigned char *)(readelf->map_address)) + vaddr);
}
}
return 0;
}
unsigned int jit_readelf_num_needed(jit_readelf_t readelf)
{
jit_dynamic_iter_t iter;
unsigned int count = 0;
jit_int type;
Elf_Addr value;
dynamic_iter_init(&iter, readelf);
while(dynamic_iter_next(&iter, &type, &value))
{
if(type == DT_NEEDED)
{
++count;
}
}
return count;
}
const char *jit_readelf_get_needed(jit_readelf_t readelf, unsigned int index)
{
jit_dynamic_iter_t iter;
jit_int type;
Elf_Addr value;
dynamic_iter_init(&iter, readelf);
while(dynamic_iter_next(&iter, &type, &value))
{
if(type == DT_NEEDED)
{
if(index == 0)
{
return get_dyn_string(readelf, value);
}
--index;
}
}
return 0;
}
void jit_readelf_add_to_context(jit_readelf_t readelf, jit_context_t context)
{
if(!readelf || !context)
{
return;
}
_jit_memory_lock(context);
readelf->next = context->elf_binaries;
context->elf_binaries = readelf;
_jit_memory_unlock(context);
}
typedef struct
{
const char *name;
void *value;
} jit_internalsym;
extern jit_internalsym const _jit_internal_symbols[];
extern int const _jit_num_internal_symbols;
static void *resolve_symbol
(jit_context_t context, jit_readelf_t readelf,
int print_failures, const char *name, jit_nuint symbol)
{
Elf_Sym *sym;
void *value;
const char *symbol_name;
jit_readelf_t library;
int index, left, right, cmp;
if(symbol >= readelf->symbol_table_size)
{
if(print_failures)
{
printf("%s: invalid symbol table index %lu\n",
name, (unsigned long)symbol);
}
return 0;
}
sym = &(readelf->symbol_table[symbol]);
if(sym->st_value)
{
value = jit_readelf_map_vaddr(readelf, (jit_nuint)(sym->st_value));
if(!value)
{
if(print_failures)
{
printf("%s: could not map virtual address 0x%lx\n",
name, (long)(sym->st_value));
}
}
return value;
}
symbol_name = get_dyn_string(readelf, sym->st_name);
if(!symbol_name)
{
if(print_failures)
{
printf("%s: symbol table index %lu does not have a valid name\n",
name, (unsigned long)symbol);
}
return 0;
}
for(index = 0; index < context->num_registered_symbols; ++index)
{
if(!jit_strcmp(symbol_name, context->registered_symbols[index]->name) &&
!(context->registered_symbols[index]->after))
{
return context->registered_symbols[index]->value;
}
}
library = context->elf_binaries;
while(library != 0)
{
value = jit_readelf_get_symbol(library, symbol_name);
if(value)
{
return value;
}
library = library->next;
}
left = 0;
right = _jit_num_internal_symbols - 1;
while(left <= right)
{
index = (left + right) / 2;
cmp = jit_strcmp(symbol_name, _jit_internal_symbols[index].name);
if(cmp == 0)
{
return _jit_internal_symbols[index].value;
}
else if(cmp < 0)
{
right = index - 1;
}
else
{
left = index + 1;
}
}
for(index = 0; index < context->num_registered_symbols; ++index)
{
if(!jit_strcmp(symbol_name, context->registered_symbols[index]->name) &&
context->registered_symbols[index]->after)
{
return context->registered_symbols[index]->value;
}
}
printf("%s: could not resolve `%s'\n", name, symbol_name);
return 0;
}
static int perform_rel
(jit_context_t context, jit_readelf_t readelf,
int print_failures, const char *name, Elf_Rel *reloc)
{
void *address;
void *value;
address = jit_readelf_map_vaddr(readelf, (jit_nuint)(reloc->r_offset));
if(!address)
{
if(print_failures)
{
printf("%s: cannot map virtual address 0x%lx\n",
name, (long)(reloc->r_offset));
}
return 0;
}
value = resolve_symbol
(context, readelf, print_failures, name,
(jit_nuint)ELF_R_SYM(reloc->r_info));
if(!value)
{
return 0;
}
if(!(*(readelf->reloc_func))
(readelf, address, (int)(ELF_R_TYPE(reloc->r_info)),
(jit_nuint)value, 0, 0))
{
if(print_failures)
{
printf("%s: relocation type %d was not recognized\n",
name, (int)(ELF_R_TYPE(reloc->r_info)));
}
return 0;
}
return 1;
}
static int perform_rela
(jit_context_t context, jit_readelf_t readelf,
int print_failures, const char *name, Elf_Rela *reloc)
{
void *address;
void *value;
address = jit_readelf_map_vaddr(readelf, (jit_nuint)(reloc->r_offset));
if(!address)
{
if(print_failures)
{
printf("%s: cannot map virtual address 0x%lx\n",
name, (long)(reloc->r_offset));
}
return 0;
}
value = resolve_symbol
(context, readelf, print_failures, name,
(jit_nuint)ELF_R_SYM(reloc->r_info));
if(!value)
{
return 0;
}
if(!(*(readelf->reloc_func))
(readelf, address, (int)(ELF_R_TYPE(reloc->r_info)),
(jit_nuint)value, 1, (jit_nuint)(reloc->r_addend)))
{
if(print_failures)
{
printf("%s: relocation type %d was not recognized\n",
name, (int)(ELF_R_TYPE(reloc->r_info)));
}
return 0;
}
return 1;
}
static int perform_relocations
(jit_context_t context, jit_readelf_t readelf, int print_failures)
{
Elf_Addr address;
Elf_Addr table_size;
Elf_Addr entry_size;
unsigned char *table;
const char *name;
int ok = 1;
name = jit_readelf_get_name(readelf);
if(!name)
{
name = "unknown-elf-binary";
}
if(!(readelf->reloc_func))
{
if(print_failures)
{
printf("%s: do not know how to perform relocations\n", name);
}
return 0;
}
if(dynamic_for_type(readelf, DT_REL, &address) &&
dynamic_for_type(readelf, DT_RELSZ, &table_size) &&
dynamic_for_type(readelf, DT_RELENT, &entry_size) && entry_size)
{
table = (unsigned char *)jit_readelf_map_vaddr
(readelf, (jit_nuint)address);
while(table && table_size >= entry_size)
{
if(!perform_rel(context, readelf, print_failures, name,
(Elf_Rel *)table))
{
ok = 0;
}
table += (jit_nuint)entry_size;
table_size -= entry_size;
}
}
if(dynamic_for_type(readelf, DT_RELA, &address) &&
dynamic_for_type(readelf, DT_RELASZ, &table_size) &&
dynamic_for_type(readelf, DT_RELAENT, &entry_size) && entry_size)
{
table = (unsigned char *)jit_readelf_map_vaddr
(readelf, (jit_nuint)address);
while(table && table_size >= entry_size)
{
if(!perform_rela(context, readelf, print_failures, name,
(Elf_Rela *)table))
{
ok = 0;
}
table += (jit_nuint)entry_size;
table_size -= entry_size;
}
}
if(dynamic_for_type(readelf, DT_JMPREL, &address) &&
dynamic_for_type(readelf, DT_PLTRELSZ, &table_size) &&
dynamic_for_type(readelf, DT_PLTREL, &entry_size))
{
if(entry_size == DT_REL)
{
if(dynamic_for_type(readelf, DT_RELENT, &entry_size) && entry_size)
{
table = (unsigned char *)jit_readelf_map_vaddr
(readelf, (jit_nuint)address);
while(table && table_size >= entry_size)
{
if(!perform_rel(context, readelf, print_failures, name,
(Elf_Rel *)table))
{
ok = 0;
}
table += (jit_nuint)entry_size;
table_size -= entry_size;
}
}
}
else if(entry_size == DT_RELA)
{
if(dynamic_for_type(readelf, DT_RELAENT, &entry_size) && entry_size)
{
table = (unsigned char *)jit_readelf_map_vaddr
(readelf, (jit_nuint)address);
while(table && table_size >= entry_size)
{
if(!perform_rela(context, readelf, print_failures, name,
(Elf_Rela *)table))
{
ok = 0;
}
table += (jit_nuint)entry_size;
table_size -= entry_size;
}
}
}
}
return ok;
}
int jit_readelf_resolve_all(jit_context_t context, int print_failures)
{
int ok = 1;
jit_readelf_t readelf;
if(!context)
{
return 0;
}
_jit_memory_lock(context);
readelf = context->elf_binaries;
while(readelf != 0)
{
if(!(readelf->resolved))
{
readelf->resolved = 1;
if(!perform_relocations(context, readelf, print_failures))
{
ok = 0;
}
}
readelf = readelf->next;
}
_jit_memory_unlock(context);
return ok;
}
int jit_readelf_register_symbol
(jit_context_t context, const char *name, void *value, int after)
{
jit_regsym_t sym;
jit_regsym_t *new_list;
if(!context || !name || !value)
{
return 0;
}
sym = (jit_regsym_t)jit_malloc
(sizeof(struct jit_regsym) + jit_strlen(name));
if(!sym)
{
return 0;
}
sym->value = value;
sym->after = after;
jit_strcpy(sym->name, name);
new_list = (jit_regsym_t *)jit_realloc
(context->registered_symbols,
sizeof(jit_regsym_t) * (context->num_registered_symbols + 1));
if(!new_list)
{
jit_free(sym);
return 0;
}
new_list[(context->num_registered_symbols)++] = sym;
context->registered_symbols = new_list;
return 1;
}
#if defined(__i386) || defined(__i386__) || defined(_M_IX86)
static int i386_reloc(jit_readelf_t readelf, void *address, int type,
jit_nuint value, int has_addend, jit_nuint addend)
{
if(type == R_386_32)
{
if(has_addend)
{
*((jit_nuint *)address) = value + addend;
}
else
{
*((jit_nuint *)address) += value;
}
return 1;
}
else if(type == R_386_PC32)
{
value -= (jit_nuint)address;
if(has_addend)
{
*((jit_nuint *)address) = value + addend;
}
else
{
*((jit_nuint *)address) += value;
}
return 1;
}
return 0;
}
#endif
#if defined(__arm) || defined(__arm__)
static int arm_reloc(jit_readelf_t readelf, void *address, int type,
jit_nuint value, int has_addend, jit_nuint addend)
{
if(type == R_ARM_PC24)
{
value -= (jit_nuint)address;
if(has_addend)
{
*((jit_nuint *)address) =
(*((jit_nuint *)address) & 0xFF000000) + value + addend;
}
else
{
*((jit_nuint *)address) += value;
}
return 1;
}
else if(type == R_ARM_ABS32)
{
if(has_addend)
{
*((jit_nuint *)address) = value + addend;
}
else
{
*((jit_nuint *)address) += value;
}
return 1;
}
else if(type == R_ARM_REL32)
{
value -= (jit_nuint)address;
if(has_addend)
{
*((jit_nuint *)address) = value + addend;
}
else
{
*((jit_nuint *)address) += value;
}
return 1;
}
return 0;
}
#endif
static int interp_reloc(jit_readelf_t readelf, void *address, int type,
jit_nuint value, int has_addend, jit_nuint addend)
{
if(type == 1)
{
*((jit_nuint *)address) = value;
return 1;
}
else
{
return 0;
}
}
static jit_reloc_func get_reloc(unsigned int machine)
{
#if defined(__i386) || defined(__i386__) || defined(_M_IX86)
if(machine == EM_386)
{
return i386_reloc;
}
#endif
#if defined(__arm) || defined(__arm__)
if(machine == EM_ARM)
{
return arm_reloc;
}
#endif
if(machine == 0x4C6A)
{
return interp_reloc;
}
return 0;
}