1 /*
2 * Copyright (c) 2006-2024 RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2018/08/29 Bernard first version
9 * 2021/04/23 chunyexixiaoyu distinguish 32-bit and 64-bit
10 */
11
12 #include "dlmodule.h"
13 #include "dlelf.h"
14
15 #define DBG_TAG "DLMD"
16 #define DBG_LVL DBG_INFO
17 #include <rtdbg.h> /* must after of DEBUG_ENABLE or some other options*/
18
19 /**
20 * @brief Load a shared object file into memory.
21 *
22 * @param module A pointer to a rt_dlmodule object for holding the module's information.
23 * @param module_ptr A pointer to the raw memory of the ELF file (shared object) that is being loaded.
24 * @return rt_err_t On success, it returns RT_EOK. Otherwise, it returns the error code.
25 *
26 * @note This function loads a shared object (ELF file) into memory, broken down into steps:
27 * 1. Initialization and Validation: it begins by validating the module pointer
28 * and checking if the ELF file has been linked by comparing its magic number (RTMMAG).
29 * If matched, the module is considered linked.
30 * 2. Calculating the ELF Image Size: it iterates over the ELF program headers to compute the total size of the ELF image
31 * by adding the sizes of loadable segments. It also ensures there are no overlaps or invalid addresses in the segments.
32 * 3. Allocating Memory for the Module: After determining the module size, the function allocates memory (module->mem_space) for the ELF image
33 * and initializes it to zero. Then, it copies the relevant program segments from the ELF file into this allocated memory.
34 * 4. Setting the Module Entry Point: it sets the entry point address (module->entry_addr) based on the ELF entry point adjusted by the calculated base address.
35 * 5. Handling Relocation Sections: It processes each relocation section in the ELF file.
36 * For each relocation entry, it either resolves the symbol from the module's own symbol table
37 * or looks up the symbol in the kernel symbol table if it was not found locally.
38 * 6. Building the Module's Symbol Table: it looks for the .dynsym section to extract global function symbols.
39 * It creates a symbol table (module->symtab) and populates it with the function names and addresses for all global symbols of type STT_FUNC.
40 * 7. Extracting Additional Parameters: It extracts additional parameters, such as thread priority (dlmodule_thread_priority) and stack size (dlmodule_thread_stacksize),
41 * from the symbol table and assigns them to the module if valid.
42 */
dlmodule_load_shared_object(struct rt_dlmodule * module,void * module_ptr)43 rt_err_t dlmodule_load_shared_object(struct rt_dlmodule* module, void *module_ptr)
44 {
45 rt_bool_t linked = RT_FALSE;
46 rt_ubase_t index, module_size = 0;
47 Elf_Addr vstart_addr, vend_addr;
48 rt_bool_t has_vstart;
49
50 RT_ASSERT(module_ptr != RT_NULL);
51
52 if (rt_memcmp(elf_module->e_ident, RTMMAG, SELFMAG) == 0)
53 {
54 /* rtmlinker finished */
55 linked = RT_TRUE;
56 }
57
58 /* get the ELF image size */
59 has_vstart = RT_FALSE;
60 vstart_addr = vend_addr = RT_NULL;
61 for (index = 0; index < elf_module->e_phnum; index++)
62 {
63 if (phdr[index].p_type != PT_LOAD)
64 continue;
65
66 LOG_D("LOAD segment: %d, 0x%p, 0x%08x", index, phdr[index].p_vaddr, phdr[index].p_memsz);
67
68 if (phdr[index].p_memsz < phdr[index].p_filesz)
69 {
70 rt_kprintf("invalid elf: segment %d: p_memsz: %d, p_filesz: %d\n",
71 index, phdr[index].p_memsz, phdr[index].p_filesz);
72 return RT_NULL;
73 }
74 if (!has_vstart)
75 {
76 vstart_addr = phdr[index].p_vaddr;
77 vend_addr = phdr[index].p_vaddr + phdr[index].p_memsz;
78 has_vstart = RT_TRUE;
79 if (vend_addr < vstart_addr)
80 {
81 LOG_E("invalid elf: segment %d: p_vaddr: %d, p_memsz: %d\n",
82 index, phdr[index].p_vaddr, phdr[index].p_memsz);
83 return RT_NULL;
84 }
85 }
86 else
87 {
88 if (phdr[index].p_vaddr < vend_addr)
89 {
90 LOG_E("invalid elf: segment should be sorted and not overlapped\n");
91 return RT_NULL;
92 }
93 if (phdr[index].p_vaddr > vend_addr + 16)
94 {
95 /* There should not be too much padding in the object files. */
96 LOG_W("warning: too much padding before segment %d", index);
97 }
98
99 vend_addr = phdr[index].p_vaddr + phdr[index].p_memsz;
100 if (vend_addr < phdr[index].p_vaddr)
101 {
102 LOG_E("invalid elf: "
103 "segment %d address overflow\n", index);
104 return RT_NULL;
105 }
106 }
107 }
108
109 module_size = vend_addr - vstart_addr;
110 LOG_D("module size: %d, vstart_addr: 0x%p", module_size, vstart_addr);
111 if (module_size == 0)
112 {
113 LOG_E("Module: size error\n");
114 return -RT_ERROR;
115 }
116
117 module->vstart_addr = vstart_addr;
118 module->nref = 0;
119
120 /* allocate module space */
121 module->mem_space = rt_malloc(module_size);
122 if (module->mem_space == RT_NULL)
123 {
124 LOG_E("Module: allocate space failed.\n");
125 return -RT_ERROR;
126 }
127 module->mem_size = module_size;
128
129 /* zero all space */
130 rt_memset(module->mem_space, 0, module_size);
131 for (index = 0; index < elf_module->e_phnum; index++)
132 {
133 if (phdr[index].p_type == PT_LOAD)
134 {
135 rt_memcpy(module->mem_space + phdr[index].p_vaddr - vstart_addr,
136 (rt_uint8_t *)elf_module + phdr[index].p_offset,
137 phdr[index].p_filesz);
138 }
139 }
140
141 /* set module entry */
142 module->entry_addr = module->mem_space + elf_module->e_entry - vstart_addr;
143
144 /* handle relocation section */
145 for (index = 0; index < elf_module->e_shnum; index ++)
146 {
147 rt_ubase_t i, nr_reloc;
148 Elf_Sym *symtab;
149 Elf_Rel *rel;
150 rt_uint8_t *strtab;
151 static rt_bool_t unsolved = RT_FALSE;
152 #if (defined(__arm__) || defined(__i386__) || (__riscv_xlen == 32))
153 if (!IS_REL(shdr[index]))
154 continue;
155 #elif (defined(__aarch64__) || defined(__x86_64__) || (__riscv_xlen == 64))
156 if (!IS_RELA(shdr[index]))
157 continue;
158 #endif
159
160 /* get relocate item */
161 rel = (Elf_Rel *)((rt_uint8_t *)module_ptr + shdr[index].sh_offset);
162
163 /* locate .rel.plt and .rel.dyn section */
164 symtab = (Elf_Sym *)((rt_uint8_t *)module_ptr +
165 shdr[shdr[index].sh_link].sh_offset);
166 strtab = (rt_uint8_t *)module_ptr +
167 shdr[shdr[shdr[index].sh_link].sh_link].sh_offset;
168 nr_reloc = (rt_ubase_t)(shdr[index].sh_size / sizeof(Elf_Rel));
169
170 /* relocate every items */
171 for (i = 0; i < nr_reloc; i ++)
172 {
173 #if (defined(__arm__) || defined(__i386__) || (__riscv_xlen == 32))
174 Elf_Sym *sym = &symtab[ELF32_R_SYM(rel->r_info)];
175 #elif (defined(__aarch64__) || defined(__x86_64__) || (__riscv_xlen == 64))
176 Elf_Sym *sym = &symtab[ELF64_R_SYM(rel->r_info)];
177 #endif
178 LOG_D("relocate symbol %s shndx %d", strtab + sym->st_name, sym->st_shndx);
179
180 if ((sym->st_shndx != SHT_NULL) ||(ELF_ST_BIND(sym->st_info) == STB_LOCAL))
181 {
182 Elf_Addr addr;
183
184 addr = (Elf_Addr)(module->mem_space + sym->st_value - vstart_addr);
185 dlmodule_relocate(module, rel, addr);
186 }
187 else if (!linked)
188 {
189 Elf_Addr addr;
190
191 LOG_D("relocate symbol: %s", strtab + sym->st_name);
192 /* need to resolve symbol in kernel symbol table */
193 addr = dlmodule_symbol_find((const char *)(strtab + sym->st_name));
194 if (addr == 0)
195 {
196 LOG_E("Module: can't find %s in kernel symbol table", strtab + sym->st_name);
197 unsolved = RT_TRUE;
198 }
199 else
200 {
201 dlmodule_relocate(module, rel, addr);
202 }
203 }
204 rel ++;
205 }
206
207 if (unsolved)
208 return -RT_ERROR;
209 }
210
211 /* construct module symbol table */
212 for (index = 0; index < elf_module->e_shnum; index ++)
213 {
214 /* find .dynsym section */
215 rt_uint8_t *shstrab;
216 shstrab = (rt_uint8_t *)module_ptr +
217 shdr[elf_module->e_shstrndx].sh_offset;
218 if (rt_strcmp((const char *)(shstrab + shdr[index].sh_name), ELF_DYNSYM) == 0)
219 break;
220 }
221
222 /* found .dynsym section */
223 if (index != elf_module->e_shnum)
224 {
225 int i, count = 0;
226 Elf_Sym *symtab = RT_NULL;
227 rt_uint8_t *strtab = RT_NULL;
228
229 symtab = (Elf_Sym *)((rt_uint8_t *)module_ptr + shdr[index].sh_offset);
230 strtab = (rt_uint8_t *)module_ptr + shdr[shdr[index].sh_link].sh_offset;
231
232 for (i = 0; i < shdr[index].sh_size / sizeof(Elf_Sym); i++)
233 {
234 if ((ELF_ST_BIND(symtab[i].st_info) == STB_GLOBAL) &&
235 (ELF_ST_TYPE(symtab[i].st_info) == STT_FUNC))
236 count ++;
237 }
238
239 module->symtab = (struct rt_module_symtab *)rt_malloc
240 (count * sizeof(struct rt_module_symtab));
241 module->nsym = count;
242 for (i = 0, count = 0; i < shdr[index].sh_size / sizeof(Elf_Sym); i++)
243 {
244 rt_size_t length;
245
246 if ((ELF_ST_BIND(symtab[i].st_info) != STB_GLOBAL) ||
247 (ELF_ST_TYPE(symtab[i].st_info) != STT_FUNC))
248 continue;
249
250 length = rt_strlen((const char *)(strtab + symtab[i].st_name)) + 1;
251
252 module->symtab[count].addr =
253 (void *)(module->mem_space + symtab[i].st_value - module->vstart_addr);
254 module->symtab[count].name = rt_malloc(length);
255 rt_memset((void *)module->symtab[count].name, 0, length);
256 rt_memcpy((void *)module->symtab[count].name,
257 strtab + symtab[i].st_name,
258 length);
259 count ++;
260 }
261
262 /* get priority & stack size params*/
263 rt_uint32_t flag = 0;
264 rt_uint16_t priority;
265 rt_uint32_t stacksize;
266 for (i = 0; i < shdr[index].sh_size / sizeof(Elf_Sym); i++)
267 {
268 if (((flag & 0x01) == 0) &&
269 (rt_strcmp((const char *)(strtab + symtab[i].st_name), "dlmodule_thread_priority") == 0))
270 {
271 flag |= 0x01;
272 priority = *(rt_uint16_t*)(module->mem_space + symtab[i].st_value - module->vstart_addr);
273 if (priority < RT_THREAD_PRIORITY_MAX)
274 {
275 module->priority = priority;
276 }
277 }
278
279 if (((flag & 0x02) == 0) &&
280 (rt_strcmp((const char *)(strtab + symtab[i].st_name), "dlmodule_thread_stacksize") == 0))
281 {
282 flag |= 0x02;
283 stacksize = *(rt_uint32_t*)(module->mem_space + symtab[i].st_value - module->vstart_addr);
284 if ((stacksize < 2048) || (stacksize > 1024 * 32))
285 {
286 module->stack_size = stacksize;
287 }
288 }
289
290 if ((flag & 0x03) == 0x03)
291 {
292 break;
293 }
294 }
295 }
296
297 return RT_EOK;
298 }
299
300 /**
301 * @brief Load a relocatable file into memory.
302 *
303 * @param module A pointer to a rt_dlmodule object for holding the module's information.
304 * @param module_ptr A pointer to the raw memory of the ELF file (relocatable file) that is being loaded.
305 * @return rt_err_t On success, it returns RT_EOK. Otherwise, it returns the error code.
306 *
307 * @note This function loads a relocatable file (ELF file) into memory, broken down step by step:
308 * 1. Calculate Module Size: iterates over the ELF sections (text, data, rodata, and bss) to calculate the total size of the module
309 * and identifies the start address for each section.
310 * 2. Allocate Memory: It allocates memory for the module based on the calculated size. If allocation fails, an error is returned.
311 * 3. Load Sections into Memory: The function loads the text, rodata, data, and BSS sections into the allocated memory.
312 * The BSS section is zeroed out, while the others are copied from the ELF image.
313 * 4. Set Entry Point: The entry point of the module is set by calculating the address relative to the start of the allocated memory.
314 * 5. Handle Relocation: It processes the relocation entries, resolving symbol addresses and relocating them as needed.
315 * This includes functions, sections (rodata, bss, data), and external symbols from the kernel symbol table.
316 */
dlmodule_load_relocated_object(struct rt_dlmodule * module,void * module_ptr)317 rt_err_t dlmodule_load_relocated_object(struct rt_dlmodule* module, void *module_ptr)
318 {
319 rt_ubase_t index, rodata_addr = 0, bss_addr = 0, data_addr = 0;
320 rt_ubase_t module_addr = 0, module_size = 0;
321 rt_uint8_t *ptr, *strtab, *shstrab;
322
323 /* get the ELF image size */
324 for (index = 0; index < elf_module->e_shnum; index ++)
325 {
326 /* text */
327 if (IS_PROG(shdr[index]) && IS_AX(shdr[index]))
328 {
329 module_size += shdr[index].sh_size;
330 module_addr = shdr[index].sh_addr;
331 }
332 /* rodata */
333 if (IS_PROG(shdr[index]) && IS_ALLOC(shdr[index]))
334 {
335 module_size += shdr[index].sh_size;
336 }
337 /* data */
338 if (IS_PROG(shdr[index]) && IS_AW(shdr[index]))
339 {
340 module_size += shdr[index].sh_size;
341 }
342 /* bss */
343 if (IS_NOPROG(shdr[index]) && IS_AW(shdr[index]))
344 {
345 module_size += shdr[index].sh_size;
346 }
347 }
348
349 /* no text, data and bss on image */
350 if (module_size == 0) return RT_NULL;
351
352 module->vstart_addr = 0;
353
354 /* allocate module space */
355 module->mem_space = rt_malloc(module_size);
356 if (module->mem_space == RT_NULL)
357 {
358 LOG_E("Module: allocate space failed.\n");
359 return -RT_ERROR;
360 }
361 module->mem_size = module_size;
362
363 /* zero all space */
364 ptr = module->mem_space;
365 rt_memset(ptr, 0, module_size);
366
367 /* load text and data section */
368 for (index = 0; index < elf_module->e_shnum; index ++)
369 {
370 /* load text section */
371 if (IS_PROG(shdr[index]) && IS_AX(shdr[index]))
372 {
373 rt_memcpy(ptr,
374 (rt_uint8_t *)elf_module + shdr[index].sh_offset,
375 shdr[index].sh_size);
376 LOG_D("load text 0x%x, size %d", ptr, shdr[index].sh_size);
377 ptr += shdr[index].sh_size;
378 }
379
380 /* load rodata section */
381 if (IS_PROG(shdr[index]) && IS_ALLOC(shdr[index]))
382 {
383 rt_memcpy(ptr,
384 (rt_uint8_t *)elf_module + shdr[index].sh_offset,
385 shdr[index].sh_size);
386 rodata_addr = (rt_ubase_t)ptr;
387 LOG_D("load rodata 0x%x, size %d, rodata 0x%x", ptr,
388 shdr[index].sh_size, *(rt_ubase_t *)rodata_addr);
389 ptr += shdr[index].sh_size;
390 }
391
392 /* load data section */
393 if (IS_PROG(shdr[index]) && IS_AW(shdr[index]))
394 {
395 rt_memcpy(ptr,
396 (rt_uint8_t *)elf_module + shdr[index].sh_offset,
397 shdr[index].sh_size);
398 data_addr = (rt_ubase_t)ptr;
399 LOG_D("load data 0x%x, size %d, data 0x%x", ptr,
400 shdr[index].sh_size, *(rt_ubase_t *)data_addr);
401 ptr += shdr[index].sh_size;
402 }
403
404 /* load bss section */
405 if (IS_NOPROG(shdr[index]) && IS_AW(shdr[index]))
406 {
407 rt_memset(ptr, 0, shdr[index].sh_size);
408 bss_addr = (rt_ubase_t)ptr;
409 LOG_D("load bss 0x%x, size %d", ptr, shdr[index].sh_size);
410 }
411 }
412
413 /* set module entry */
414 module->entry_addr = (rt_dlmodule_entry_func_t)((rt_uint8_t *)module->mem_space + elf_module->e_entry - module_addr);
415
416 /* handle relocation section */
417 for (index = 0; index < elf_module->e_shnum; index ++)
418 {
419 rt_ubase_t i, nr_reloc;
420 Elf_Sym *symtab;
421 Elf_Rel *rel;
422
423 #if (defined(__arm__) || defined(__i386__) || (__riscv_xlen == 32))
424 if (!IS_REL(shdr[index]))
425 continue;
426 #elif (defined(__aarch64__) || defined(__x86_64__) || (__riscv_xlen == 64))
427 if (!IS_RELA(shdr[index]))
428 continue;
429 #endif
430
431
432 /* get relocate item */
433 rel = (Elf_Rel *)((rt_uint8_t *)module_ptr + shdr[index].sh_offset);
434
435 /* locate .dynsym and .dynstr */
436 symtab = (Elf_Sym *)((rt_uint8_t *)module_ptr +
437 shdr[shdr[index].sh_link].sh_offset);
438 strtab = (rt_uint8_t *)module_ptr +
439 shdr[shdr[shdr[index].sh_link].sh_link].sh_offset;
440 shstrab = (rt_uint8_t *)module_ptr +
441 shdr[elf_module->e_shstrndx].sh_offset;
442 nr_reloc = (rt_uint32_t)(shdr[index].sh_size / sizeof(Elf_Rel));
443
444 /* relocate every items */
445 for (i = 0; i < nr_reloc; i ++)
446 {
447 #if (defined(__arm__) || defined(__i386__) || (__riscv_xlen == 32))
448 Elf_Sym *sym = &symtab[ELF32_R_SYM(rel->r_info)];
449 #elif (defined(__aarch64__) || defined(__x86_64__) || (__riscv_xlen == 64))
450 Elf_Sym *sym = &symtab[ELF64_R_SYM(rel->r_info)];
451 #endif
452
453 LOG_D("relocate symbol: %s", strtab + sym->st_name);
454
455 if (sym->st_shndx != STN_UNDEF)
456 {
457 Elf_Addr addr = 0;
458
459 if ((ELF_ST_TYPE(sym->st_info) == STT_SECTION) ||
460 (ELF_ST_TYPE(sym->st_info) == STT_OBJECT))
461 {
462 if (rt_strncmp((const char *)(shstrab +
463 shdr[sym->st_shndx].sh_name), ELF_RODATA, 8) == 0)
464 {
465 /* relocate rodata section */
466 LOG_D("rodata");
467 addr = (Elf_Addr)(rodata_addr + sym->st_value);
468 }
469 else if (rt_strncmp((const char *)
470 (shstrab + shdr[sym->st_shndx].sh_name), ELF_BSS, 5) == 0)
471 {
472 /* relocate bss section */
473 LOG_D("bss");
474 addr = (Elf_Addr)bss_addr + sym->st_value;
475 }
476 else if (rt_strncmp((const char *)(shstrab + shdr[sym->st_shndx].sh_name),
477 ELF_DATA, 6) == 0)
478 {
479 /* relocate data section */
480 LOG_D("data");
481 addr = (Elf_Addr)data_addr + sym->st_value;
482 }
483
484 if (addr != 0) dlmodule_relocate(module, rel, addr);
485 }
486 else if (ELF_ST_TYPE(sym->st_info) == STT_FUNC)
487 {
488 addr = (Elf_Addr)((rt_uint8_t *) module->mem_space - module_addr + sym->st_value);
489
490 /* relocate function */
491 dlmodule_relocate(module, rel, addr);
492 }
493 }
494 else if (ELF_ST_TYPE(sym->st_info) == STT_FUNC)
495 {
496 /* relocate function */
497 dlmodule_relocate(module, rel,
498 (Elf_Addr)((rt_uint8_t *)
499 module->mem_space
500 - module_addr
501 + sym->st_value));
502 }
503 else
504 {
505 Elf_Addr addr;
506
507 if (ELF32_R_TYPE(rel->r_info) != R_ARM_V4BX)
508 {
509 LOG_D("relocate symbol: %s", strtab + sym->st_name);
510
511 /* need to resolve symbol in kernel symbol table */
512 addr = dlmodule_symbol_find((const char *)(strtab + sym->st_name));
513 if (addr != (Elf_Addr)RT_NULL)
514 {
515 dlmodule_relocate(module, rel, addr);
516 LOG_D("symbol addr 0x%x", addr);
517 }
518 else
519 LOG_E("Module: can't find %s in kernel symbol table",
520 strtab + sym->st_name);
521 }
522 else
523 {
524 addr = (Elf_Addr)((rt_uint8_t *) module->mem_space - module_addr + sym->st_value);
525 dlmodule_relocate(module, rel, addr);
526 }
527 }
528
529 rel ++;
530 }
531 }
532
533 return RT_EOK;
534 }
535