1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2017 Andes Technology 4 * Chih-Mao Chen <cmchen (a] 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 27 static 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 38 static 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