android_kernel_samsung_univ.../init/ld.c

701 lines
17 KiB
C
Raw Permalink Normal View History

/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <asm/cacheflush.h>
#include <asm/irqflags.h>
#include <linux/fs.h>
#include <asm/tlbflush.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/vmalloc.h>
#ifdef CONFIG_UH
#include <linux/uh.h>
#endif
#include "ld.h"
#include "elf.h"
//#define LD_DEBUG
#ifdef LD_DEBUG
#define ld_log(a, ...) printk(KERN_ALERT a, __VA_ARGS__)
#else
#define ld_log(a, ...)
#endif //LD_DEBUG
int __init ld_Elf_Ehdr_to_Elf_Shdr(_Elf_Ehdr *ehdr, _Elf_Shdr **shdr, size_t *size)
{
ld_log("%s\n", __FUNCTION__);
if(ehdr == NULL) { return -1; }
if(shdr == NULL) { return -1; }
if(size == NULL) { return -1; }
*shdr = (_Elf_Shdr *)((unsigned long)ehdr + (unsigned long)ehdr->e_shoff);
*size = ehdr->e_shnum;
return 0;
}
int __init ld_Elf_Ehdr_to_Elf_Phdr(_Elf_Ehdr *ehdr, _Elf_Phdr **phdr, size_t *size)
{
ld_log("%s\n", __FUNCTION__);
if(ehdr == NULL) { return -1; }
if(phdr == NULL) { return -1; }
if(size == NULL) { return -1; }
*phdr = (_Elf_Phdr *)((unsigned long)ehdr + (unsigned long)(ehdr->e_phoff));
*size = ehdr->e_phnum;
return 0;
}
int __init ld_binary_to_Elf_Ehdr(void *binary, _Elf_Ehdr **ehdr)
{
ld_log("%s\n", __FUNCTION__);
if(ehdr == NULL) { return -1; }
*ehdr = (_Elf_Ehdr *)binary;
return 0;
}
int __init ld_get_name(void *binary, char **name)
{
_Elf_Dyn *dyn;
char *strtab;
size_t sz;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(name == NULL) { return -1; }
if(ld_get_sect(binary, ".dynamic", (void **)&dyn, &sz)) { return -1; }
if(ld_get_dynamic_strtab(binary, &strtab, &sz)) { return -1; }
*name = NULL;
for(i = 0; i < (sz / sizeof(_Elf_Dyn)); i++) {
switch(dyn[i].d_tag) {
case _DT_SONAME:
*name = &strtab[dyn[i].d_un.d_val];
break;
}
if(*name != NULL) { break; }
}
return 0;
}
int __init ld_get_version(void *binary, char **version)
{
_Elf_Sym *symtab;
char *strtab;
size_t strtabsz;
size_t symtabsz;
char *name;
char **string;
_Elf_Sym *sym;
void *base = NULL;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(ld_get_dynamic_strtab(binary, &strtab, &strtabsz)) { return -1; }
if(ld_get_dynamic_symtab(binary, &symtab, &symtabsz)) { return -1; }
for(i = 0; i < (symtabsz / sizeof(_Elf_Sym)); i++) {
if(ld_get_symbol(symtab, i, &sym)) { return -1; }
if(ld_get_string(strtab, sym->st_name, &name)) { return -1; }
if(strcmp(name, "version") == 0) {
if(ld_get_base(binary, &base)) { return -1; }
string = (char **)((unsigned long)sym->st_value + (unsigned long)base);
*version = *string;
return 0;
}
}
return -1;
}
int __init ld_get_string(char *strtab, int index, char **string)
{
ld_log("%s\n", __FUNCTION__);
if(strtab == NULL) { return -1; }
if(string == NULL) { return -1; }
*string = &(((char *)strtab)[index]);
return 0;
}
int __init ld_get_symbol(_Elf_Sym *symtab, int index, _Elf_Sym **symbol)
{
ld_log("%s\n", __FUNCTION__);
if(symtab == NULL) { return -1; }
if(symbol == NULL) { return -1; }
*symbol = &(symtab[index]);
return 0;
}
int __init ld_get_base(void *binary, void **address)
{
_Elf_Ehdr *ehdr;
_Elf_Phdr *phdr;
int set;
size_t size;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(address == NULL) { return -1; }
if(ld_binary_to_Elf_Ehdr(binary, &ehdr)) { return -1; }
if(ld_Elf_Ehdr_to_Elf_Phdr(ehdr, &phdr, &size)) { return -1; }
set = 0;
for(i = 0; i < ehdr->e_phnum; i++) {
if(phdr[i].p_type == _PT_LOAD) {
if((phdr[i].p_offset < (_Elf_Off)((unsigned long)*address)) || set == 0) {
*address = (void *)(unsigned long)phdr[i].p_offset;
set = 1;
}
}
}
*address = (void *)((long)*address + (long)binary);
return 0;
}
int __init ld_get_size(void *binary, size_t *size)
{
_Elf_Ehdr *ehdr;
_Elf_Phdr *phdr;
int set;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(size == NULL) { return -1; }
if(ld_binary_to_Elf_Ehdr(binary, &ehdr)) { return -1; }
if(ld_Elf_Ehdr_to_Elf_Phdr(ehdr, &phdr, size)) { return -1; }
set = 0;
for(i = 0; i < ehdr->e_phnum; i++) {
if(phdr[i].p_type == _PT_LOAD) {
if((phdr[i].p_vaddr + phdr[i].p_memsz) > (_Elf_Off)*size) {
*size = (size_t)phdr[i].p_vaddr + (size_t)phdr[i].p_memsz;
}
}
}
*size = *size - UH_START;
return 0;
}
int __init ld_get_sect(void *binary, char *name, void **section, size_t *size)
{
_Elf_Ehdr *ehdr;
_Elf_Shdr *shdr;
size_t sz;
void *strtab;
char *tmp;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(name == NULL) { return -1; }
if(section == NULL) { return -1; }
if(size == NULL) { return -1; }
if(ld_binary_to_Elf_Ehdr(binary, &ehdr)) { return -1; }
if(ld_Elf_Ehdr_to_Elf_Shdr(ehdr, &shdr, &sz)) { return -1; }
strtab = (void *)((unsigned long)binary + (unsigned long)shdr[ehdr->e_shstrndx].sh_offset);
for(i = 0; i < sz; i++) {
if(ld_get_string(strtab, shdr[i].sh_name, &tmp)) { return -1; }
if(strcmp(name, tmp) == 0) {
*section = (void *)((unsigned long)binary + (unsigned long)shdr[i].sh_offset - (unsigned long)UH_START);
*size = shdr[i].sh_size;
return 0;
}
}
*section = NULL;
return -1;
}
int __init ld_get_dynamic_symtab(void *binary, _Elf_Sym **symtab, size_t *size)
{
_Elf_Dyn *dyn;
size_t sz;
size_t ent;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(symtab == NULL) { return -1; }
if(size == NULL) { return -1; }
if(ld_get_sect(binary, ".dynamic", (void **)&dyn, &sz)) { return -1; }
*symtab = NULL;
*size = 0;
ent = 0;
for(i = 0; i < (sz / sizeof(_Elf_Dyn)); i++) {
switch(dyn[i].d_tag) {
case _DT_SYMTAB:
*symtab = (void *)((unsigned long)dyn[i].d_un.d_ptr);
break;
case _DT_SYMENT:
ent = dyn[i].d_un.d_val;
break;
}
if((*symtab != NULL) && (*size != 0) && (ent != 0)) { break; }
}
if(ent != sizeof(_Elf_Sym)) { return -1; }
*symtab = (void *)((long)*symtab + (long)binary);
if(ld_get_sect(binary, ".dynsym", (void **)&dyn, &sz)) { return -1; }
if(sz % sizeof(_Elf_Sym) != 0) { return -1; }
*size = sz;
return 0;
}
int __init ld_get_dynamic_strtab(void *binary, char **strtab, size_t *size)
{
_Elf_Dyn *dyn;
size_t sz;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(strtab == NULL) { return -1; }
if(size == NULL) { return -1; }
if(ld_get_sect(binary, ".dynamic", (void **)&dyn, &sz)) { return -1; }
*strtab = NULL;
*size = 0;
for(i = 0; i < (sz / sizeof(_Elf_Dyn)); i++) {
switch(dyn[i].d_tag) {
case _DT_STRTAB:
*strtab = (void *)((unsigned long)dyn[i].d_un.d_ptr);
break;
case _DT_STRSZ:
*size = dyn[i].d_un.d_val;
break;
}
if((*strtab != NULL) && (*size != 0)) { break; }
}
*strtab = (void *)((long)*strtab + (long)binary);
if(ld_get_sect(binary, ".dynstr", (void **)&dyn, &sz)) { return -1; }
if(*size != sz) { return -1; }
return 0;
}
#ifdef __TARGET_64__
int __init ld_get_dynamic_relatab(void *binary, _Elf_Rela **relatab, size_t *size)
{
_Elf_Dyn *dyn;
size_t sz;
size_t ent;
unsigned int i;
//printk(KERN_ALERT "%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(relatab == NULL) { return -1; }
if(size == NULL) { return -1; }
if(ld_get_sect(binary, ".dynamic", (void **)&dyn, &sz)) { return -1; }
*relatab = NULL;
*size = 0;
ent = 0;
for(i = 0; i < (sz / sizeof(_Elf_Dyn)); i++) {
switch(dyn[i].d_tag) {
case _DT_RELA:
*relatab = (void *)(unsigned long)dyn[i].d_un.d_ptr;
break;
case _DT_RELASZ:
*size = dyn[i].d_un.d_val;
break;
case _DT_RELAENT:
ent = dyn[i].d_un.d_val;
break;
}
if((*relatab != NULL) && (*size != 0) && (ent != 0)) { break; }
}
ld_log("*relatab: %p, *size: %d, ent: %d\n", *relatab, (int)*size, (int)ent);
if(ent != sizeof(_Elf_Rela)) { return -1; }
*relatab = (void *)((long)*relatab + (long)binary);
return 0;
}
#else //__TARGET_32__
int __init ld_get_dynamic_reltab(void *binary, _Elf_Rel **reltab, size_t *size)
{
_Elf_Dyn *dyn;
size_t sz;
size_t ent;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(reltab == NULL) { return -1; }
if(size == NULL) { return -1; }
if(ld_get_sect(binary, ".dynamic", (void **)&dyn, &sz)) { return -1; }
*reltab = NULL;
*size = 0;
ent = 0;
for(i = 0; i < (sz / sizeof(_Elf_Dyn)); i++) {
switch(dyn[i].d_tag) {
case _DT_REL:
*reltab = (void *)dyn[i].d_un.d_ptr;
break;
case _DT_RELSZ:
*size = dyn[i].d_un.d_val;
break;
case _DT_RELENT:
ent = dyn[i].d_un.d_val;
break;
}
if((*reltab != NULL) && (*size != 0) && (ent != 0)) { break; }
}
if(ent != sizeof(_Elf_Rel)) { return -1; }
*reltab = (void *)((long)*reltab + (long)binary);
return 0;
}
#endif //__TARGET_64__ | __TARGET_32__
#ifdef __TARGET_64__
int __init ld_get_dynamic_plttab(void *binary, _Elf_Rela **plttab, size_t *size)
#else //__TARGET_32__
int __init ld_get_dynamic_plttab(void *binary, _Elf_Rel **plttab, size_t *size)
#endif //__TARGET_64__ | __TARGET_32__
{
_Elf_Dyn *dyn;
size_t sz;
int type;
unsigned int i;
ld_log("%s\n", __FUNCTION__);
if(binary == NULL) { return -1; }
if(plttab == NULL) { return -1; }
if(size == NULL) { return -1; }
if(ld_get_sect(binary, ".dynamic", (void **)&dyn, &sz)) { return -1; }
*plttab = NULL;
*size = 0;
type = 0;
for(i = 0; i < (sz / sizeof(_Elf_Dyn)); i++) {
switch(dyn[i].d_tag) {
case _DT_JMPREL:
*plttab = (void *)((unsigned long)dyn[i].d_un.d_ptr);
break;
case _DT_PLTREL:
type = dyn[i].d_un.d_val;
break;
case _DT_PLTRELSZ:
*size = dyn[i].d_un.d_val;
break;
}
if((*plttab != NULL) && (*size != 0) && (type != 0)) { break; }
}
#ifdef __TARGET_64__
if((type != _DT_RELA) || ((*size % sizeof(_Elf_Rela)) != 0)) { return -1; }
#else //__TARGET_32_
if((type != _DT_REL) || ((*size % sizeof(_Elf_Rel)) != 0)) { return -1; }
#endif //__TARGET_64__ | __TARGET_32__
*plttab = (void *)((long)*plttab + (long)binary);
return 0;
}
#ifdef __TARGET_64__
int __init ld_fixup_dynamic_relatab(void *binary, ld_resolve_t resolve, ld_translate_t translate)
{
_Elf_Rela *relatab;
_Elf_Sym *symtab;
size_t relatabsz;
size_t symtabsz;
_Elf_Sym *sym;
unsigned int i;
void *base = NULL;
void *runtime;
_Elf_Sxword *pointer;
_Elf_Sxword value;
//printk(KERN_ALERT "%s\n", __FUNCTION__);
if(ld_get_base(binary, &base)) { return -1; }
if(ld_get_dynamic_relatab(binary, &relatab, &relatabsz)) { return 0; }
ld_log("relatab %p, relatabsz: %d\n", relatab, (int)relatabsz);
if(ld_get_dynamic_symtab(binary, &symtab, &symtabsz)) { return -1; }
ld_log("symtab %p, symtabsz: %d\n", symtab, (int)symtabsz);
if(translate(binary, base, &runtime)) { return -1; }
ld_log("base: %p, runtime: %p\n", base, runtime);
for(i = 0; i < (relatabsz / sizeof(_Elf_Rela)); i++) {
switch(_ELF_R_TYPE(relatab[i].r_info)) {
case _R_AARCH64_RELATIVE:
pointer = (_Elf_Sxword *)((_Elf_Sxword)base + ((unsigned long)relatab[i].r_offset));
value = (_Elf_Sxword)runtime + ((unsigned long)relatab[i].r_addend);
*((_Elf_Sxword *)pointer) = value;
ld_log("fixed up _R_AARCH64_RELATIVE: %p, %p\n", (void *)pointer, (void *)value);
break;
case _R_AARCH64_GLOB_DAT:
if(ld_get_symbol(symtab, _ELF_R_SYM(relatab[i].r_info), &sym)) { return -1; }
pointer = (_Elf_Sxword *)((_Elf_Sxword)base + ((unsigned long)relatab[i].r_offset));
ld_log("fixed up R_ARM_GLOB_DAT: %p, %p\n", (void *)pointer, (void *)value);
if(sym->st_shndx == _SHN_UNDEF) {
if(resolve(binary, sym, &value) != 0) { return -1; }
}
else { value = (_Elf_Sxword)runtime + (_Elf_Sword)sym->st_value; }
*((_Elf_Sxword *)pointer) = value;
ld_log("fixed up R_ARM_GLOB_DAT: %p, %p\n", (void *)pointer, (void *)value);
break;
default:
ld_log("unsupported relocate: %d\n", (int)_ELF_R_TYPE(relatab[i].r_info));
return -1;
}
}
return 0;
}
#else //__TARGET_32__
int __init ld_fixup_dynamic_reltab(void *binary, ld_resolve_t resolve, ld_translate_t translate)
{
_Elf_Rel *reltab;
_Elf_Sym *symtab;
size_t reltabsz;
size_t symtabsz;
_Elf_Sym *sym;
unsigned int i;
void *base = NULL;
void *runtime;
_Elf_Sword *pointer;
_Elf_Sword value;
ld_log("%s\n", __FUNCTION__);
if(ld_get_base(binary, &base)) { return -1; }
if(ld_get_dynamic_reltab(binary, &reltab, &reltabsz)) { return 0; }
ld_log("reltab 0x%x, reltabsz: %d\n", reltab, (int)reltabsz);
if(ld_get_dynamic_symtab(binary, &symtab, &symtabsz)) { return -1; }
ld_log("symtab 0x%x, symtabsz: %d\n", symtab, (int)symtabsz);
if(translate(binary, base, &runtime)) { return -1; }
ld_log("base: %p, runtime: %p\n", base, runtime);
for(i = 0; i < (reltabsz / sizeof(_Elf_Rel)); i++) {
switch(_ELF_R_TYPE(reltab[i].r_info)) {
case _R_ARM_RELATIVE:
pointer = (_Elf_Sword *)((_Elf_Sword)base + (_Elf_Sword)reltab[i].r_offset);
value = (_Elf_Sword)runtime + *pointer;
*((_Elf_Sword *)pointer) = value;
ld_log("fixed up _R_ARM_RELATIVE: 0x%x, 0x%x\n", pointer, (void *)value);
break;
case _R_ARM_ABS32:
if(ld_get_symbol(symtab, _ELF_R_SYM(reltab[i].r_info), &sym)) { return -1; }
pointer = (_Elf_Sword *)((_Elf_Sword)runtime + (_Elf_Sword)reltab[i].r_offset);
if(sym->st_shndx == _SHN_UNDEF) {
if(resolve(binary, sym, &value) != 0) { return -1; }
}
else { value = (_Elf_Sword)runtime + (_Elf_Sword)sym->st_value; }
*((_Elf_Sword *)pointer) = value;
ld_log("fixed up _R_ARM_ABS32: 0x%x, 0x%x\n", pointer, (void *)value);
break;
case _R_ARM_GLOB_DAT:
if(ld_get_symbol(symtab, _ELF_R_SYM(reltab[i].r_info), &sym)) { return -1; }
pointer = (_Elf_Sword *)((_Elf_Sword)base + (_Elf_Sword)reltab[i].r_offset);
if(sym->st_shndx == _SHN_UNDEF) {
if(resolve(binary, sym, &value) != 0) { return -1; }
}
else { value = (_Elf_Sword)runtime + (_Elf_Sword)sym->st_value; }
*((_Elf_Sword *)pointer) = value;
ld_log("fixed up _R_ARM_GLOB_DAT: 0x%x, 0x%x\n", pointer, (void *)value);
break;
default:
ld_log("unsupported relocate: %d\n", (int)_ELF_R_TYPE(reltab[i].r_info));
return -1;
}
}
return 0;
}
#endif //__TARGET_64__ | __TARGET_32__
int __init ld_fixup_dynamic_plttab(void *binary, ld_resolve_t resolve, ld_translate_t translate)
{
#ifdef __TARGET_64__
_Elf_Rela *plttab;
#else //__TARGET_64__
_Elf_Rel *plttab;
#endif //__TARGET_64__ | __TARGET_32__
_Elf_Sym *symtab;
_Elf_Sym *sym;
size_t plttabsz;
size_t symtabsz;
unsigned int i;
void *base = NULL;
void *runtime;
#ifdef __TARGET_64__
_Elf_Sxword *pointer;
_Elf_Sxword value;
#else //__TARGET_32__
_Elf_Sword *pointer;
_Elf_Sword value;
#endif //__TARGET_64__ | __TARGET_32__
ld_log("%s\n", __FUNCTION__);
if(ld_get_base(binary, &base)) { return -1; }
if(ld_get_dynamic_plttab(binary, &plttab, &plttabsz)) { return 0; }
ld_log("plttab: %p, plttabsz: %d\n", plttab, (int)plttabsz);
if(ld_get_dynamic_symtab(binary, &symtab, &symtabsz)) { return -1; }
ld_log("symtab: %p, symtabsz: %d\n", symtab, (int)symtabsz);
if(translate(binary, base, &runtime)) { return -1; }
ld_log("base: %p, runtime: %p\n", base, runtime);
#ifdef __TARGET_64__
for(i = 0; i < (plttabsz / sizeof(_Elf_Rela)); i++) {
#else //__TARGET_32__
for(i = 0; i < (plttabsz / sizeof(_Elf_Rel)); i++) {
#endif //__TARGET_64__ | __TARGET_32__
switch(_ELF_R_TYPE(plttab[i].r_info)) {
#ifdef __TARGET_64__
case _R_AARCH64_JUMP_SLOT:
if(ld_get_symbol(symtab, _ELF_R_SYM(plttab[i].r_info), &sym)) { return -1; }
pointer = (_Elf_Sxword *)((_Elf_Sxword)base + ((unsigned long)plttab[i].r_offset));
if(sym->st_shndx == _SHN_UNDEF) {
if(resolve(binary, sym, &value) != 0) { return -1; }
}
else { value = (_Elf_Sxword)runtime + (_Elf_Sword)sym->st_value; }
*((_Elf_Sxword *)pointer) = value;
ld_log("fixed up _R_AARCH64_JUMP_SLOT: %p, %p\n", (void *)pointer, (void *)value);
break;
#else //__TARGET_32__
case _R_ARM_JUMP_SLOT:
if(ld_get_symbol(symtab, _ELF_R_SYM(plttab[i].r_info), &sym)) { return -1; }
pointer = (_Elf_Sword *)((_Elf_Sword)base + (_Elf_Sword)plttab[i].r_offset);
if(sym->st_shndx == _SHN_UNDEF) {
if(resolve(binary, sym, &value) != 0) { return -1; }
}
else { value = (_Elf_Sword)runtime + (_Elf_Sword)sym->st_value; }
*((_Elf_Sword *)pointer) = value;
ld_log("fixed up _R_ARM_JUMP_SLOT: 0x%x, 0x%x\n", pointer, (void *)value);
break;
#endif //__TARGET_64__ | __TARGET_32__
default:
ld_log("unsupported relocation: %d\n", (int)_ELF_R_TYPE(plttab[i].r_info));
return -1;
}
}
return 0;
}