Home | History | Annotate | Download | only in tools
      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