/* * 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 #include #include #include #include #include #include #include #include #include #ifdef CONFIG_UH #include #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; }