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