support more relocation type in elfloader

mostly copied from linux kernel 4.20, Shh...
This commit is contained in:
daishengdong
2020-06-09 20:26:12 +08:00
parent 0a2d5a4e90
commit 5e16690f7b
5 changed files with 172 additions and 16 deletions

View File

@@ -20,18 +20,19 @@
typedef enum elfloader_err_en {
ELFLOADER_ERR_NONE,
ELFLOADER_ERR_FD_READ_FAILED,
ELFLOADER_ERR_HEADER_INVALID,
ELFLOADER_ERR_TYPE_INVALID,
ELFLOADER_ERR_NO_DYN,
ELFLOADER_ERR_NO_SYMTAB,
ELFLOADER_ERR_NO_STRTAB,
ELFLOADER_ERR_NO_TEXT,
ELFLOADER_ERR_NO_LOAD_SEGMENTS,
ELFLOADER_ERR_FD_READ_FAILED,
ELFLOADER_ERR_OUT_OF_MEMORY,
ELFLOADER_ERR_RELOCATE_FAILED,
ELFLOADER_ERR_PTR_NULL,
ELFLOADER_ERR_SECTION_NOT_FOUND,
ELFLOADER_ERR_SYM_NOT_FOUND,
ELFLOADER_ERR_OUT_OF_MEMORY,
ELFLOADER_ERR_PTR_NULL,
ELFLOADER_ERR_TYPE_INVALID,
} el_err_t;
#endif /* _TOS_ELFLOADER_ERR_H_ */

View File

@@ -18,7 +18,7 @@
#ifndef _TOS_ELFLOADER_RELOCATE_H_
#define _TOS_ELFLOADER_RELOCATE_H_
__KNL__ void elfloader_arch_relocate(uint32_t reloc_addr, int32_t load_bias, uint32_t addr, elf32_rela_t *rela, int is_rela);
__KNL__ el_err_t elfloader_arch_relocate(uint32_t reloc_addr, int32_t load_bias, uint32_t addr, elf32_rela_t *rela, int is_rela, elf32_sym_t *sym);
#endif /* _TOS_ELFLOADER_RELOCATE_H_ */

View File

@@ -18,11 +18,23 @@
#include "tos_elfloader.h"
/* values for ELF32_R_TYPE(info) */
#define R_ARM_NONE 0
#define R_ARM_PC24 1
#define R_ARM_ABS32 2
#define R_ARM_THM_CALL 10
#define R_ARM_GLOB_DAT 21
#define R_ARM_JUMP_SLOT 22
#define R_ARM_RELATIVE 23
#define R_ARM_CALL 28
#define R_ARM_JUMP24 29
#define R_ARM_THM_JUMP24 30
#define R_ARM_TARGET1 38
#define R_ARM_V4BX 40
#define R_ARM_PREL31 42
#define R_ARM_MOVW_ABS_NC 43
#define R_ARM_MOVT_ABS 44
#define R_ARM_THM_MOVW_ABS_NC 47
#define R_ARM_THM_MOVT_ABS 48
/*
- S (when used on its own) is the address of the symbol.
@@ -38,33 +50,172 @@ addresses). This value must always be word-aligned. See §4.6.1.8, Proxy generat
- GOT(S) is the address of the GOT entry for the symbol S.
*/
// TODO: support more relocation type
__KNL__ void elfloader_arch_relocate(uint32_t reloc_addr, int32_t load_bias, uint32_t addr, elf32_rela_t *rela, int is_rela)
__KNL__ el_err_t elfloader_arch_relocate(uint32_t reloc_addr, int32_t load_bias, uint32_t addr, elf32_rela_t *rela, int is_rela, elf32_sym_t *sym)
{
/* ATTENTION:
different reloc_addr calculation algorithm for relocatable object and shared object
*/
int32_t offset;
uint32_t tmp;
uint32_t upper, lower, sign, j1, j2;
switch (ELF32_R_TYPE(rela->r_info)) {
case R_ARM_NONE:
/* ignore */
break;
case R_ARM_ABS32:
case R_ARM_TARGET1:
/* (S + A) | T */
*(uint32_t *)reloc_addr += addr;
break;
case R_ARM_PC24:
case R_ARM_CALL:
case R_ARM_JUMP24:
/* ((S + A) | T) P */
if (addr & 3) {
tos_kprintf("unsupported interworking call(ARM -> Thumb)\n");
return ELFLOADER_ERR_RELOCATE_FAILED;
}
offset = *(uint32_t *)reloc_addr;
offset = (offset & 0x00ffffff) << 2;
if (offset & 0x02000000) {
offset -= 0x04000000;
}
offset += addr - reloc_addr;
if (offset <= (int32_t)0xfe000000 || offset >= (int32_t)0x02000000) {
tos_kprintf("relocation out of range\n");
return ELFLOADER_ERR_RELOCATE_FAILED;
}
offset >>= 2;
offset &= 0x00ffffff;
*(uint32_t *)reloc_addr &= 0xff000000;
*(uint32_t *)reloc_addr |= offset;
break;
case R_ARM_V4BX:
*(uint32_t *)reloc_addr &= 0xf000000f;
*(uint32_t *)reloc_addr |= 0x01a0f000;
break;
case R_ARM_PREL31:
/* ((S + A) | T) P */
offset = (*(int32_t *)reloc_addr << 1) >> 1; /* sign extend */
offset += addr - reloc_addr;
if (offset >= 0x40000000 || offset < -0x40000000) {
tos_kprintf("relocation out of range\n");
return ELFLOADER_ERR_RELOCATE_FAILED;
}
*(uint32_t *)reloc_addr &= 0x80000000;
*(uint32_t *)reloc_addr |= offset & 0x7fffffff;
break;
case R_ARM_GLOB_DAT:
case R_ARM_JUMP_SLOT:
/* (S + A) | T */
*(uint32_t *)reloc_addr = addr;
break;
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVT_ABS:
offset = tmp = *(uint32_t *)reloc_addr;
offset = ((offset & 0xf0000) >> 4) | (offset & 0xfff);
offset = (offset ^ 0x8000) - 0x8000;
offset += addr;
if (ELF32_R_TYPE(rela->r_info) == R_ARM_MOVT_ABS) {
offset >>= 16;
}
tmp &= 0xfff0f000;
tmp |= ((offset & 0xf000) << 4) | (offset & 0x0fff);
*(uint32_t *)reloc_addr = tmp;
break;
case R_ARM_THM_CALL:
case R_ARM_THM_JUMP24:
if (ELF32_SYM_TYPE(sym->st_info) == STT_FUNC && !(addr & 1)) {
tos_kprintf("unsupported interworking call(Thumb -> ARM)\n");
return ELFLOADER_ERR_RELOCATE_FAILED;
}
upper = *(uint16_t *)reloc_addr;
lower = *(uint16_t *)(reloc_addr + 2);
sign = (upper >> 10) & 1;
j1 = (lower >> 13) & 1;
j2 = (lower >> 11) & 1;
offset = (sign << 24) | ((~(j1 ^ sign) & 1) << 23) |
((~(j2 ^ sign) & 1) << 22) |
((upper & 0x03ff) << 12) |
((lower & 0x07ff) << 1);
if (offset & 0x01000000) {
offset -= 0x02000000;
}
offset += addr - reloc_addr;
if (offset <= (int32_t)0xff000000 || offset >= (int32_t)0x01000000) {
tos_kprintf("relocation out of range\n");
return ELFLOADER_ERR_RELOCATE_FAILED;
}
sign = (offset >> 24) & 1;
j1 = sign ^ (~(offset >> 23) & 1);
j2 = sign ^ (~(offset >> 22) & 1);
upper = (uint16_t)((upper & 0xf800) | (sign << 10) |
((offset >> 12) & 0x03ff));
lower = (uint16_t)((lower & 0xd000) |
(j1 << 13) | (j2 << 11) |
((offset >> 1) & 0x07ff));
*(uint16_t *)reloc_addr = (uint16_t)upper;
*(uint16_t *)(reloc_addr + 2) = (uint16_t)lower;
break;
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_THM_MOVT_ABS:
upper = *(uint16_t *)reloc_addr;
lower = *(uint16_t *)(reloc_addr + 2);
offset = ((upper & 0x000f) << 12) |
((upper & 0x0400) << 1) |
((lower & 0x7000) >> 4) | (lower & 0x00ff);
offset = (offset ^ 0x8000) - 0x8000;
offset += addr;
if (ELF32_R_TYPE(rela->r_info) == R_ARM_THM_MOVT_ABS) {
offset >>= 16;
}
upper = (uint16_t)((upper & 0xfbf0) |
((offset & 0xf000) >> 12) |
((offset & 0x0800) >> 1));
lower = (uint16_t)((lower & 0x8f00) |
((offset & 0x0700) << 4) |
(offset & 0x00ff));
*(uint16_t *)reloc_addr = (uint16_t)upper;
*(uint16_t *)(reloc_addr + 2) = (uint16_t)lower;
break;
case R_ARM_RELATIVE:
/* B(S) + A */
*(uint32_t *)reloc_addr += load_bias;
break;
case R_ARM_ABS32:
/* (S + A) | T */
*(uint32_t *)reloc_addr += addr;
break;
default:
printf("Unsupported Relocation Type: %d\n", ELF32_R_TYPE(rela->r_info));
tos_kprintf("unsupported relocation type: %d\n", ELF32_R_TYPE(rela->r_info));
break;
}
return ELFLOADER_ERR_NONE;
}

View File

@@ -136,7 +136,9 @@ __STATIC__ el_err_t elfloader_relocate(int fd, void *address,
return ELFLOADER_ERR_SYM_NOT_FOUND;
}
elfloader_arch_relocate(rela.r_offset + (uint32_t)address, 0, (uint32_t)addr, &rela, is_rela);
if (elfloader_arch_relocate(rela.r_offset + (uint32_t)address, 0, (uint32_t)addr, &rela, is_rela, &sym) != ELFLOADER_ERR_NONE) {
return ELFLOADER_ERR_RELOCATE_FAILED;
}
rel_offset += rel_entsize;
}

View File

@@ -109,7 +109,9 @@ __STATIC__ el_err_t elfloader_relocate(int fd, int32_t load_bias,
return ELFLOADER_ERR_SYM_NOT_FOUND;
}
elfloader_arch_relocate(rela.r_offset + load_bias, load_bias, (uint32_t)addr, &rela, is_rela);
if (elfloader_arch_relocate(rela.r_offset + load_bias, load_bias, (uint32_t)addr, &rela, is_rela, &sym) != ELFLOADER_ERR_NONE) {
return ELFLOADER_ERR_RELOCATE_FAILED;
}
rel_offset += rel_entsize;
}
@@ -249,7 +251,7 @@ __API__ el_err_t tos_elfloader_load(el_module_t *module, int fd)
}
/* reserving memory for LOAD segments */
base = tos_mmheap_aligned_alloc(vaddr_end - vaddr_start, 64);
base = tos_mmheap_alloc(vaddr_end - vaddr_start);
if (!base) {
return ELFLOADER_ERR_OUT_OF_MEMORY;
}