1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Copyright (C) 2017 Andes Technology 4 * Chih-Mao Chen <cmchen@andestech.com> 5 * 6 * Statically process runtime relocations on RISC-V ELF images 7 * so that it can be directly executed when loaded at LMA 8 * without fixup. Both RV32 and RV64 are supported. 9 */ 10 11#define CONCAT_IMPL(x, y) x##y 12#define CONCAT(x, y) CONCAT_IMPL(x, y) 13#define CONCAT3(x, y, z) CONCAT(CONCAT(x, y), z) 14 15#define prelink_nn CONCAT(prelink, PRELINK_INC_BITS) 16#define uintnn_t CONCAT3(uint, PRELINK_INC_BITS, _t) 17#define get_offset_nn CONCAT(get_offset_, PRELINK_INC_BITS) 18#define Elf_Ehdr CONCAT3(Elf, PRELINK_INC_BITS, _Ehdr) 19#define Elf_Phdr CONCAT3(Elf, PRELINK_INC_BITS, _Phdr) 20#define Elf_Rela CONCAT3(Elf, PRELINK_INC_BITS, _Rela) 21#define Elf_Sym CONCAT3(Elf, PRELINK_INC_BITS, _Sym) 22#define Elf_Dyn CONCAT3(Elf, PRELINK_INC_BITS, _Dyn) 23#define Elf_Addr CONCAT3(Elf, PRELINK_INC_BITS, _Addr) 24#define ELF_R_TYPE CONCAT3(ELF, PRELINK_INC_BITS, _R_TYPE) 25#define ELF_R_SYM CONCAT3(ELF, PRELINK_INC_BITS, _R_SYM) 26 27static void* get_offset_nn (void* data, Elf_Phdr* phdrs, size_t phnum, Elf_Addr addr) 28{ 29 Elf_Phdr *p; 30 31 for (p = phdrs; p < phdrs + phnum; ++p) 32 if (p->p_vaddr <= addr && p->p_vaddr + p->p_memsz > addr) 33 return data + p->p_offset + (addr - p->p_vaddr); 34 35 return NULL; 36} 37 38static void prelink_nn(void *data) 39{ 40 Elf_Ehdr *ehdr = data; 41 Elf_Phdr *p; 42 Elf_Dyn *dyn; 43 Elf_Rela *r; 44 45 if (ehdr->e_machine != EM_RISCV) 46 die("Machine type is not RISC-V"); 47 48 Elf_Phdr *phdrs = data + ehdr->e_phoff; 49 50 Elf_Dyn *dyns = NULL; 51 for (p = phdrs; p < phdrs + ehdr->e_phnum; ++p) { 52 if (p->p_type == PT_DYNAMIC) { 53 dyns = data + p->p_offset; 54 break; 55 } 56 } 57 58 if (dyns == NULL) 59 die("No dynamic section found"); 60 61 Elf_Rela *rela_dyn = NULL; 62 size_t rela_count = 0; 63 Elf_Sym *dynsym = NULL; 64 for (dyn = dyns;; ++dyn) { 65 if (dyn->d_tag == DT_NULL) 66 break; 67 else if (dyn->d_tag == DT_RELA) 68 rela_dyn = get_offset_nn(data, phdrs, ehdr->e_phnum, + dyn->d_un.d_ptr); 69 else if (dyn->d_tag == DT_RELASZ) 70 rela_count = dyn->d_un.d_val / sizeof(Elf_Rela); 71 else if (dyn->d_tag == DT_SYMTAB) 72 dynsym = get_offset_nn(data, phdrs, ehdr->e_phnum, + dyn->d_un.d_ptr); 73 74 } 75 76 if (rela_dyn == NULL) 77 die("No .rela.dyn found"); 78 79 if (dynsym == NULL) 80 die("No .dynsym found"); 81 82 for (r = rela_dyn; r < rela_dyn + rela_count; ++r) { 83 void* buf = get_offset_nn(data, phdrs, ehdr->e_phnum, r->r_offset); 84 85 if (buf == NULL) 86 continue; 87 88 if (ELF_R_TYPE(r->r_info) == R_RISCV_RELATIVE) 89 *((uintnn_t*) buf) = r->r_addend; 90 else if (ELF_R_TYPE(r->r_info) == R_RISCV_32) 91 *((uint32_t*) buf) = dynsym[ELF_R_SYM(r->r_info)].st_value; 92 else if (ELF_R_TYPE(r->r_info) == R_RISCV_64) 93 *((uint64_t*) buf) = dynsym[ELF_R_SYM(r->r_info)].st_value; 94 } 95} 96 97#undef prelink_nn 98#undef uintnn_t 99#undef get_offset_nn 100#undef Elf_Ehdr 101#undef Elf_Phdr 102#undef Elf_Rela 103#undef Elf_Sym 104#undef Elf_Dyn 105#undef Elf_Addr 106#undef ELF_R_TYPE 107#undef ELF_R_SYM 108 109#undef CONCAT_IMPL 110#undef CONCAT 111#undef CONCAT3 112