Files
TencentOS-tiny/components/elfloader/shared_object/tos_elfloader_shared_object.c
daishengdong 0a2d5a4e90 support elfloader for shared object and relocatable object
1. elfloader for shared object
example: TencentOS-tiny\examples\elfloader_shared_object
keil: TencentOS-tiny\board\TencentOS_tiny_EVB_MX_Plus\KEIL\elfloader_shared_object

2. elfloader for relocatable object:
example: TencentOS-tiny\examples\elfloader_relocatable_object
keil: TencentOS-tiny\board\TencentOS_tiny_EVB_MX_Plus\KEIL\elfloader_relocatable_object

3. TODO:
- add icache/dcache flush when module is loaded
- support more relocation type in elfloader_arch_relocate
2020-06-09 19:30:38 +08:00

354 lines
12 KiB
C

/*----------------------------------------------------------------------------
* Tencent is pleased to support the open source community by making TencentOS
* available.
*
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
* If you have downloaded a copy of the TencentOS binary from Tencent, please
* note that the TencentOS binary is licensed under the BSD 3-Clause License.
*
* If you have downloaded a copy of the TencentOS source code from Tencent,
* please note that TencentOS source code is licensed under the BSD 3-Clause
* License, except for the third-party components listed below which are
* subject to different license terms. Your integration of TencentOS into your
* own projects may require compliance with the BSD 3-Clause License, as well
* as the other licenses applicable to the third-party components included
* within TencentOS.
*---------------------------------------------------------------------------*/
#include "tos_elfloader.h"
__STATIC__ void *local_symtab_lookup(int fd, int32_t load_bias, char *sym_name,
uint32_t symtab_offset, uint32_t symtab_size, uint32_t symtab_entsize,
uint32_t strtab_offset)
{
int i = 0;
elf32_sym_t sym;
#define SYMBOL_NAME_MAX 30
static char name[SYMBOL_NAME_MAX];
for (i = 0; i < symtab_size / symtab_entsize; ++i) {
if (elfloader_fd_read(fd, symtab_offset, &sym, sizeof(elf32_sym_t)) != ELFLOADER_ERR_NONE) {
return K_NULL;
}
if (sym.st_name) {
if (elfloader_fd_read(fd, strtab_offset + sym.st_name, name, sizeof(name)) != ELFLOADER_ERR_NONE) {
return K_NULL;
}
if (strcmp(name, sym_name) == 0) {
return (void *)(sym.st_value + load_bias);
}
}
symtab_offset += symtab_entsize;
}
return K_NULL;
}
__STATIC__ el_err_t elfloader_relocate(int fd, int32_t load_bias,
uint32_t rel_offset, uint32_t rel_size, uint32_t rel_entsize,
uint32_t symtab_offset, uint32_t symtab_size, uint32_t symtab_entsize,
uint32_t strtab_offset, uint32_t strtab_size)
{
int i = 0;
elf32_rela_t rela;
elf32_sym_t sym;
void *addr;
#define SYMBOL_NAME_MAX 30
static char name[SYMBOL_NAME_MAX];
int is_rela = (rel_entsize == sizeof(elf32_rela_t) ? K_TRUE : K_FALSE);
for (i = 0; i < rel_size / rel_entsize; ++i) {
addr = K_NULL;
if (elfloader_fd_read(fd, rel_offset, &rela, rel_entsize) != ELFLOADER_ERR_NONE) {
return ELFLOADER_ERR_FD_READ_FAILED;
}
if (elfloader_fd_read(fd,
symtab_offset + ELF32_R_SYM(rela.r_info) * symtab_entsize,
&sym,
symtab_entsize) != ELFLOADER_ERR_NONE) {
return ELFLOADER_ERR_FD_READ_FAILED;
}
/* load the symbol's name */
if (elfloader_fd_read(fd,
strtab_offset + sym.st_name,
name,
sizeof(name)) != ELFLOADER_ERR_NONE) {
return ELFLOADER_ERR_FD_READ_FAILED;
}
/* an external symbol, or a weak one */
if (sym.st_shndx == SHN_UNDEF ||
ELF32_SYM_TYPE(sym.st_info) == STB_WEAK) {
/* look up in the global symbol table */
addr = elfloader_symtab_lookup(name);
}
/* an external symbol but not found in the global symbol table */
if (sym.st_shndx == SHN_UNDEF && !addr) {
return ELFLOADER_ERR_SYM_NOT_FOUND;
}
/* an internal symbol, or a weak symbol without STRONG one in global symbol table */
if (!addr) {
addr = local_symtab_lookup(fd, load_bias, name,
symtab_offset, symtab_size, symtab_entsize,
strtab_offset);
}
/* still not found */
if (!addr) {
return ELFLOADER_ERR_SYM_NOT_FOUND;
}
elfloader_arch_relocate(rela.r_offset + load_bias, load_bias, (uint32_t)addr, &rela, is_rela);
rel_offset += rel_entsize;
}
return ELFLOADER_ERR_NONE;
}
__API__ el_err_t tos_elfloader_load(el_module_t *module, int fd)
{
int i = 0;
el_err_t err;
elf32_ehdr_t ehdr;
elf32_shdr_t shdr;
elf32_dyn_t dyn;
elf32_phdr_t phdr;
void *base = K_NULL, *addr_seg2cp; /* ram base for LOAD segments */
int32_t load_bias;
uint32_t shdr_offset, phdr_offset;
uint32_t vaddr_start = (uint32_t)-1, vaddr_end = 0;
uint32_t dyn_offset, dyn_size = 0, dyn_entsize;
uint32_t strtab_offset, strtab_size = 0;
uint32_t symtab_offset, symtab_size = 0, symtab_entsize;
uint32_t rel_entsize;
uint32_t rel_dyn_offset, rel_dyn_size = 0;
uint32_t rel_plt_offset, rel_plt_size = 0;
if (!module) {
return ELFLOADER_ERR_PTR_NULL;
}
memset(module, 0, sizeof(el_module_t));
/* read the elf header */
if (elfloader_fd_read(fd, 0, &ehdr, sizeof(elf32_ehdr_t)) != ELFLOADER_ERR_NONE) {
return ELFLOADER_ERR_FD_READ_FAILED;
}
/* sanity check, magic verify */
if (memcmp(ehdr.e_ident, elf_header_magic, sizeof(elf_header_magic)) != 0) {
return ELFLOADER_ERR_HEADER_INVALID;
}
/* it should be a shared object */
if (ehdr.e_type != ET_DYN) {
return ELFLOADER_ERR_TYPE_INVALID;
}
shdr_offset = ehdr.e_shoff;
for (i = 0; i < ehdr.e_shnum; ++i) {
if (elfloader_fd_read(fd, shdr_offset, &shdr, sizeof(elf32_shdr_t)) != ELFLOADER_ERR_NONE) {
return ELFLOADER_ERR_FD_READ_FAILED;
}
if (shdr.sh_type == SHT_DYNAMIC) { /* dynamic section, .dynamic */
dyn_offset = shdr.sh_offset;
dyn_size = shdr.sh_size;
dyn_entsize = shdr.sh_entsize;
} else if (shdr.sh_type == SHT_DYNSYM) { /* .dynsym */
symtab_size = shdr.sh_size;
}
shdr_offset += ehdr.e_shentsize;
}
if (dyn_size == 0) {
return ELFLOADER_ERR_NO_DYN;
}
if (symtab_size == 0) {
return ELFLOADER_ERR_NO_SYMTAB;
}
for (i = 0; i < dyn_size / dyn_entsize; ++i) {
if (elfloader_fd_read(fd, dyn_offset, &dyn, sizeof(elf32_dyn_t)) != ELFLOADER_ERR_NONE) {
return ELFLOADER_ERR_FD_READ_FAILED;
}
if (dyn.d_tag == DT_NULL) {
break;
} else if (dyn.d_tag == DT_SYMTAB) { /* dynamic symbol table */
symtab_offset = dyn.d_un.d_ptr;
} else if (dyn.d_tag == DT_SYMENT) { /* entry size of symbol table */
symtab_entsize = dyn.d_un.d_val;
} else if (dyn.d_tag == DT_STRTAB) { /* dynamic string table */
strtab_offset = dyn.d_un.d_ptr;
} else if (dyn.d_tag == DT_STRSZ) { /* size of dynamic string table */
strtab_size = dyn.d_un.d_val;
} else if (dyn.d_tag == DT_REL) { /* dynamic relocation table */
rel_dyn_offset = dyn.d_un.d_ptr;
} else if (dyn.d_tag == DT_RELSZ) { /* size of rel.dyn */
rel_dyn_size = dyn.d_un.d_val;
} else if (dyn.d_tag == DT_RELENT) { /* entry size of rel.dyn */
rel_entsize = dyn.d_un.d_val;
} else if (dyn.d_tag == DT_JMPREL) { /* plt relocation table. why NOT DT_PLTREL, confusing */
rel_plt_offset = dyn.d_un.d_ptr;
} else if (dyn.d_tag == DT_PLTRELSZ) { /* size of rel.plt */
rel_plt_size = dyn.d_un.d_val;
}
dyn_offset += dyn_entsize;
}
if (strtab_size == 0) {
return ELFLOADER_ERR_NO_STRTAB;
}
phdr_offset = ehdr.e_phoff;
for (i = 0; i < ehdr.e_phnum; ++i) {
if (elfloader_fd_read(fd, phdr_offset, &phdr, sizeof(elf32_phdr_t)) != ELFLOADER_ERR_NONE) {
return ELFLOADER_ERR_FD_READ_FAILED;
}
if (phdr.p_type == PHT_LOAD) {
if (phdr.p_vaddr < vaddr_start) {
vaddr_start = phdr.p_vaddr;
}
if (phdr.p_vaddr + phdr.p_memsz > vaddr_end) {
vaddr_end = phdr.p_vaddr + phdr.p_memsz;
}
}
phdr_offset += ehdr.e_phentsize;
}
if (vaddr_start == (uint32_t)-1 || vaddr_end == 0) {
return ELFLOADER_ERR_NO_LOAD_SEGMENTS;
}
/* reserving memory for LOAD segments */
base = tos_mmheap_aligned_alloc(vaddr_end - vaddr_start, 64);
if (!base) {
return ELFLOADER_ERR_OUT_OF_MEMORY;
}
load_bias = (uint32_t)base - vaddr_start;
/* do segments load */
phdr_offset = ehdr.e_phoff;
for (i = 0; i < ehdr.e_phnum; ++i) {
if (elfloader_fd_read(fd, phdr_offset, &phdr, sizeof(elf32_phdr_t)) != ELFLOADER_ERR_NONE) {
err = ELFLOADER_ERR_FD_READ_FAILED;
goto OUT;
}
if (phdr.p_type == PHT_LOAD) {
addr_seg2cp = (void *)(load_bias + phdr.p_vaddr);
if (elfloader_fd_read(fd,
phdr.p_offset,
addr_seg2cp,
phdr.p_filesz) != ELFLOADER_ERR_NONE) {
err = ELFLOADER_ERR_FD_READ_FAILED;
goto OUT;
}
if (phdr.p_memsz > phdr.p_filesz) {
/* clear bss */
memset((void *)((uint8_t *)addr_seg2cp + phdr.p_filesz),
0,
phdr.p_memsz - phdr.p_filesz);
}
}
phdr_offset += ehdr.e_phentsize;
}
if (rel_dyn_size > 0) {
err = elfloader_relocate(fd, load_bias,
rel_dyn_offset, rel_dyn_size, rel_entsize,
symtab_offset, symtab_size, symtab_entsize,
strtab_offset, strtab_size);
if (err != ELFLOADER_ERR_NONE) {
goto OUT;
}
}
if (rel_plt_size > 0) {
err = elfloader_relocate(fd, load_bias,
rel_plt_offset, rel_plt_size, rel_entsize,
symtab_offset, symtab_size, symtab_entsize,
strtab_offset, strtab_size);
if (err != ELFLOADER_ERR_NONE) {
goto OUT;
}
}
/* TODO: should do icache/dcahe flush here, sth. like:
dcache_flush();
icache_flush();
*/
module->fd = fd;
module->base = base;
module->info.so.load_bias = load_bias;
module->symtab_offset = symtab_offset;
module->symtab_size = symtab_size;
module->symtab_entsize = symtab_entsize;
module->strtab_offset = strtab_offset;
return ELFLOADER_ERR_NONE;
OUT:
if (base) {
tos_mmheap_free(base);
}
return err;
}
__API__ el_err_t tos_elfloader_unload(el_module_t *module)
{
if (!module || !module->base) {
return ELFLOADER_ERR_PTR_NULL;
}
tos_mmheap_free(module->base);
module->base = K_NULL;
return ELFLOADER_ERR_NONE;
}
__API__ void *tos_elfloader_find_symbol(el_module_t *module, char *symbol)
{
return local_symtab_lookup(module->fd, module->info.so.load_bias, symbol,
module->symtab_offset, module->symtab_size, module->symtab_entsize,
module->strtab_offset);
}