exynos-linux-stable/init/ld.c

785 lines
17 KiB
C
Raw 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.
*
*/
#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 //CONFIG_UH
#include "ld.h"
#include "elf.h"
//#define LD_DEBUG
#ifdef LD_DEBUG
#define ld_log(a, ...) pr_alert(a, __VA_ARGS__)
#else
#define ld_log(a, ...)
#endif //LD_DEBUG
int __init ld_Elf_Ehdr_to_Elf_Shdr(struct _Elf_Ehdr *ehdr, struct _Elf_Shdr **shdr, size_t *size)
{
ld_log("%s\n", __func__);
if (ehdr == NULL)
return -1;
if (shdr == NULL)
return -1;
if (size == NULL)
return -1;
*shdr = (struct _Elf_Shdr *)((unsigned long)ehdr + (unsigned long)ehdr->e_shoff);
*size = ehdr->e_shnum;
return 0;
}
int __init ld_Elf_Ehdr_to_Elf_Phdr(struct _Elf_Ehdr *ehdr, struct _Elf_Phdr **phdr, size_t *size)
{
ld_log("%s\n", __func__);
if (ehdr == NULL)
return -1;
if (phdr == NULL)
return -1;
if (size == NULL)
return -1;
*phdr = (struct _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, struct _Elf_Ehdr **ehdr)
{
ld_log("%s\n", __func__);
if (ehdr == NULL)
return -1;
*ehdr = (struct _Elf_Ehdr *)binary;
return 0;
}
int __init ld_get_name(void *binary, char **name)
{
struct _Elf_Dyn *dyn;
char *strtab;
size_t sz;
size_t i;
ld_log("%s\n", __func__);
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(struct _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)
{
struct _Elf_Sym *symtab;
char *strtab;
size_t strtabsz;
size_t symtabsz;
size_t i;
char *name;
char **string;
struct _Elf_Sym *sym;
void *base = NULL;
ld_log("%s\n", __func__);
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(struct _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", __func__);
if (strtab == NULL)
return -1;
if (string == NULL)
return -1;
*string = &(((char *)strtab)[index]);
return 0;
}
int __init ld_get_symbol(struct _Elf_Sym *symtab, int index, struct _Elf_Sym **symbol)
{
ld_log("%s\n", __func__);
if (symtab == NULL)
return -1;
if (symbol == NULL)
return -1;
*symbol = &(symtab[index]);
return 0;
}
int __init ld_get_base(void *binary, void **address)
{
struct _Elf_Ehdr *ehdr;
struct _Elf_Phdr *phdr;
int set;
size_t size;
unsigned int i;
ld_log("%s\n", __func__);
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 < (unsigned long long)((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)
{
struct _Elf_Ehdr *ehdr;
struct _Elf_Phdr *phdr;
int set;
unsigned int i;
ld_log("%s\n", __func__);
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) > (unsigned long long)*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)
{
struct _Elf_Ehdr *ehdr;
struct _Elf_Shdr *shdr;
size_t sz;
size_t i;
void *strtab;
char *tmp;
ld_log("%s\n", __func__);
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);
*size = shdr[i].sh_size;
return 0;
}
}
*section = NULL;
return -1;
}
int __init ld_get_dynamic_symtab(void *binary, struct _Elf_Sym **symtab, size_t *size)
{
struct _Elf_Dyn *dyn;
size_t sz;
size_t ent;
size_t i;
ld_log("%s\n", __func__);
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(struct _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(struct _Elf_Sym))
return -1;
*symtab = (void *)((long)*symtab + (long)binary);
if (ld_get_sect(binary, ".dynsym", (void **)&dyn, &sz))
return -1;
if (sz % sizeof(struct _Elf_Sym) != 0)
return -1;
*size = sz;
return 0;
}
int __init ld_get_dynamic_strtab(void *binary, char **strtab, size_t *size)
{
struct _Elf_Dyn *dyn;
size_t sz;
size_t i;
ld_log("%s\n", __func__);
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(struct _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, struct _Elf_Rela **relatab, size_t *size)
{
struct _Elf_Dyn *dyn;
size_t sz;
size_t ent;
size_t i;
//printk(KERN_ALERT "%s\n", __func__);
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(struct _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(struct _Elf_Rela))
return -1;
*relatab = (void *)((long)*relatab + (long)binary);
return 0;
}
#else //__TARGET_32__
int __init ld_get_dynamic_reltab(void *binary, struct _Elf_Rel **reltab, size_t *size)
{
struct _Elf_Dyn *dyn;
size_t sz;
size_t ent;
unsigned int i;
ld_log("%s\n", __func__);
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(struct _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(struct _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, struct _Elf_Rela **plttab, size_t *size)
#else //__TARGET_32__
int __init ld_get_dynamic_plttab(void *binary, struct _Elf_Rel **plttab, size_t *size)
#endif //__TARGET_64__ | __TARGET_32__
{
struct _Elf_Dyn *dyn;
size_t sz;
size_t i;
int type;
ld_log("%s\n", __func__);
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(struct _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(struct _Elf_Rela)) != 0))
return -1;
#else //__TARGET_32_
if ((type != _DT_REL) || ((*size % sizeof(struct _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)
{
struct _Elf_Rela *relatab;
struct _Elf_Sym *symtab;
size_t relatabsz;
size_t symtabsz;
size_t i;
struct _Elf_Sym *sym;
void *base = NULL;
void *runtime;
long long *pointer;
long long value;
//printk(KERN_ALERT "%s\n", __func__);
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(struct _Elf_Rela)); i++) {
switch (_ELF_R_TYPE(relatab[i].r_info)) {
case _R_AARCH64_RELATIVE:
pointer = (long long *)((long long)base + ((unsigned long)relatab[i].r_offset));
value = (long long)runtime + ((unsigned long)relatab[i].r_addend);
*((long long *)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 = (long long *)((long long)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 = (long long)runtime + (int)sym->st_value;
}
*((long long *)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)
{
struct _Elf_Rel *reltab;
struct _Elf_Sym *symtab;
size_t reltabsz;
size_t symtabsz;
struct _Elf_Sym *sym;
unsigned int i;
void *base = NULL;
void *runtime;
int *pointer;
int value;
ld_log("%s\n", __func__);
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(struct _Elf_Rel)); i++) {
switch (_ELF_R_TYPE(reltab[i].r_info)) {
case _R_ARM_RELATIVE:
pointer = (int *)((int)base + (int)reltab[i].r_offset);
value = (int)runtime + *pointer;
*((int *)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 = (int *)((int)runtime + (int)reltab[i].r_offset);
if (sym->st_shndx == _SHN_UNDEF) {
if (resolve(binary, sym, &value) != 0)
return -1;
} else {
value = (int)runtime + (int)sym->st_value;
}
*((int *)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 = (int *)((int)base + (int)reltab[i].r_offset);
if (sym->st_shndx == _SHN_UNDEF) {
if (resolve(binary, sym, &value) != 0)
return -1;
} else {
value = (int)runtime + (int)sym->st_value;
}
*((int *)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__
struct _Elf_Rela *plttab;
#else //__TARGET_64__
struct _Elf_Rel *plttab;
#endif //__TARGET_64__ | __TARGET_32__
struct _Elf_Sym *symtab;
struct _Elf_Sym *sym;
size_t plttabsz;
size_t symtabsz;
size_t i;
void *base = NULL;
void *runtime;
#ifdef __TARGET_64__
long long *pointer;
long long value;
#else //__TARGET_32__
int *pointer;
int value;
#endif //__TARGET_64__ | __TARGET_32__
ld_log("%s\n", __func__);
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(struct _Elf_Rela)); i++) {
#else //__TARGET_32__
for (i = 0; i < (plttabsz / sizeof(struct _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 = (long long *)((long long)base + ((unsigned long)plttab[i].r_offset));
if (sym->st_shndx == _SHN_UNDEF) {
if (resolve(binary, sym, &value) != 0)
return -1;
} else {
value = (long long)runtime + (int)sym->st_value;
}
*((long long *)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 = (int *)((int)base + (int)plttab[i].r_offset);
if (sym->st_shndx == _SHN_UNDEF) {
if (resolve(binary, sym, &value) != 0)
return -1;
} else {
value = (int)runtime + (int)sym->st_value;
}
*((int *)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;
}