1 /*
2  * Copyright (c) 2023 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/llext/elf.h>
8 #include <zephyr/llext/llext.h>
9 #include <zephyr/llext/llext_internal.h>
10 #include <zephyr/llext/loader.h>
11 #include <zephyr/logging/log.h>
12 
13 LOG_MODULE_DECLARE(llext, CONFIG_LLEXT_LOG_LEVEL);
14 
15 /*
16  * ELF relocation tables on Xtensa contain relocations of different types. They
17  * specify how the relocation should be performed. Which relocations are used
18  * depends on the type of the ELF object (e.g. shared or partially linked
19  * object), structure of the object (single or multiple source files), compiler
20  * flags used (e.g. -fPIC), etc. Also not all relocation table entries should be
21  * acted upon. Some of them describe relocations that have already been
22  * resolved by the linker. We have to distinguish them from actionable
23  * relocations and only need to handle the latter ones.
24  */
25 #define R_XTENSA_NONE           0
26 #define R_XTENSA_32             1
27 #define R_XTENSA_RTLD           2
28 #define R_XTENSA_GLOB_DAT       3
29 #define R_XTENSA_JMP_SLOT       4
30 #define R_XTENSA_RELATIVE       5
31 #define R_XTENSA_PLT            6
32 #define R_XTENSA_ASM_EXPAND	11
33 #define R_XTENSA_SLOT0_OP	20
34 
xtensa_elf_relocate(struct llext_loader * ldr,struct llext * ext,const elf_rela_t * rel,uintptr_t addr,uint8_t * loc,int type,uint32_t stb,const struct llext_load_param * ldr_parm)35 static int xtensa_elf_relocate(struct llext_loader *ldr, struct llext *ext,
36 			       const elf_rela_t *rel, uintptr_t addr,
37 			       uint8_t *loc, int type, uint32_t stb,
38 			       const struct llext_load_param *ldr_parm)
39 {
40 	elf_word *got_entry = (elf_word *)loc;
41 
42 	switch (type) {
43 	case R_XTENSA_RELATIVE:
44 		if (ldr_parm->pre_located) {
45 			/* Relative relocations are already correct in the pre-located case */
46 			break;
47 		}
48 
49 		/* Relocate a local symbol: Xtensa specific. Seems to only be used with PIC */
50 		unsigned int sh_ndx;
51 
52 		for (sh_ndx = 0; sh_ndx < ext->sect_cnt; sh_ndx++) {
53 			if (ext->sect_hdrs[sh_ndx].sh_addr <= *got_entry &&
54 			    *got_entry <
55 			    ext->sect_hdrs[sh_ndx].sh_addr + ext->sect_hdrs[sh_ndx].sh_size) {
56 				break;
57 			}
58 		}
59 
60 		if (sh_ndx == ext->sect_cnt) {
61 			LOG_ERR("%#x not found in any of the sections", *got_entry);
62 			return -ENOENT;
63 		}
64 
65 		*got_entry += (uintptr_t)llext_loaded_sect_ptr(ldr, ext, sh_ndx) -
66 			ext->sect_hdrs[sh_ndx].sh_addr;
67 		break;
68 	case R_XTENSA_GLOB_DAT:
69 	case R_XTENSA_JMP_SLOT:
70 		if (stb == STB_GLOBAL) {
71 			*got_entry = addr;
72 		}
73 		break;
74 	case R_XTENSA_32:
75 		/* Used for both LOCAL and GLOBAL bindings */
76 		*got_entry += addr;
77 		break;
78 	case R_XTENSA_SLOT0_OP:
79 		/* Apparently only actionable with LOCAL bindings */
80 		;
81 		elf_sym_t rsym;
82 		int ret = llext_seek(ldr, ldr->sects[LLEXT_MEM_SYMTAB].sh_offset +
83 				     ELF_R_SYM(rel->r_info) * sizeof(elf_sym_t));
84 
85 		if (!ret) {
86 			ret = llext_read(ldr, &rsym, sizeof(elf_sym_t));
87 		}
88 		if (ret) {
89 			LOG_ERR("Failed to read a symbol table entry, LLEXT linking might fail.");
90 			return ret;
91 		}
92 
93 		/*
94 		 * So far in all observed use-cases
95 		 * llext_loaded_sect_ptr(ldr, ext, rsym.st_shndx) was already
96 		 * available as the "addr" argument of this function, supplied
97 		 * by arch_elf_relocate_local() from its non-STT_SECTION branch.
98 		 */
99 		uintptr_t link_addr = (uintptr_t)llext_loaded_sect_ptr(ldr, ext, rsym.st_shndx) +
100 			rsym.st_value + rel->r_addend;
101 		ssize_t value = (link_addr - (((uintptr_t)got_entry + 3) & ~3)) >> 2;
102 
103 		/* Check the opcode */
104 		if ((loc[0] & 0xf) == 1 && !loc[1] && !loc[2]) {
105 			/* L32R: low nibble is 1 */
106 			loc[1] = value & 0xff;
107 			loc[2] = (value >> 8) & 0xff;
108 		} else if ((loc[0] & 0xf) == 5 && !(loc[0] & 0xc0) && !loc[1] && !loc[2]) {
109 			/* CALLn: low nibble is 5 */
110 			loc[0] = (loc[0] & 0x3f) | ((value << 6) & 0xc0);
111 			loc[1] = (value >> 2) & 0xff;
112 			loc[2] = (value >> 10) & 0xff;
113 		} else {
114 			LOG_DBG("%p: unhandled OPC or no relocation %02x%02x%02x inf %#x offs %#x",
115 				(void *)loc, loc[2], loc[1], loc[0],
116 				rel->r_info, rel->r_offset);
117 			break;
118 		}
119 
120 		break;
121 	case R_XTENSA_ASM_EXPAND:
122 		/* Nothing to do */
123 		break;
124 	default:
125 		LOG_DBG("Unsupported relocation type %u", type);
126 
127 		return 0;
128 	}
129 
130 	LOG_DBG("Applied relocation to %#x type %u at %p",
131 		*(uint32_t *)((uintptr_t)got_entry & ~3), type, (void *)got_entry);
132 
133 	return 0;
134 }
135 
136 /**
137  * @brief Architecture specific function for STB_LOCAL ELF relocations
138  */
arch_elf_relocate_local(struct llext_loader * ldr,struct llext * ext,const elf_rela_t * rel,const elf_sym_t * sym,uint8_t * rel_addr,const struct llext_load_param * ldr_parm)139 int arch_elf_relocate_local(struct llext_loader *ldr, struct llext *ext, const elf_rela_t *rel,
140 			    const elf_sym_t *sym, uint8_t *rel_addr,
141 			    const struct llext_load_param *ldr_parm)
142 {
143 	int type = ELF32_R_TYPE(rel->r_info);
144 	uintptr_t sh_addr;
145 
146 	if (ELF_ST_TYPE(sym->st_info) == STT_SECTION) {
147 		elf_shdr_t *shdr = ext->sect_hdrs + sym->st_shndx;
148 
149 		/* shdr->sh_addr is NULL when not built for a specific address */
150 		sh_addr = shdr->sh_addr &&
151 			(!ldr_parm->section_detached || !ldr_parm->section_detached(shdr)) ?
152 			shdr->sh_addr : (uintptr_t)llext_loaded_sect_ptr(ldr, ext, sym->st_shndx);
153 	} else {
154 		sh_addr = ldr->sects[LLEXT_MEM_TEXT].sh_addr;
155 	}
156 
157 	return xtensa_elf_relocate(ldr, ext, rel, sh_addr, rel_addr, type,
158 				   ELF_ST_BIND(sym->st_info), ldr_parm);
159 }
160 
161 /**
162  * @brief Architecture specific function for STB_GLOBAL ELF relocations
163  */
arch_elf_relocate_global(struct llext_loader * ldr,struct llext * ext,const elf_rela_t * rel,const elf_sym_t * sym,uint8_t * rel_addr,const void * link_addr)164 int arch_elf_relocate_global(struct llext_loader *ldr, struct llext *ext, const elf_rela_t *rel,
165 			     const elf_sym_t *sym, uint8_t *rel_addr, const void *link_addr)
166 {
167 	int type = ELF32_R_TYPE(rel->r_info);
168 
169 	/* For global relocations we expect the initial value for R_XTENSA_RELATIVE to be zero */
170 	if (type == R_XTENSA_RELATIVE && *(elf_word *)rel_addr) {
171 		LOG_WRN("global: non-zero relative value %#x", *(elf_word *)rel_addr);
172 	}
173 
174 	return xtensa_elf_relocate(ldr, ext, rel, (uintptr_t)link_addr, rel_addr, type,
175 				   ELF_ST_BIND(sym->st_info), NULL);
176 }
177