diff --git a/components/libc/posix/libdl/dlelf.c b/components/libc/posix/libdl/dlelf.c index 6490b3263395..5b11f151a0c2 100644 --- a/components/libc/posix/libdl/dlelf.c +++ b/components/libc/posix/libdl/dlelf.c @@ -300,6 +300,48 @@ rt_err_t dlmodule_load_shared_object(struct rt_dlmodule* module, void *module_pt return RT_EOK; } +/** + * @brief Compute the size of the common block of a symbol table, and + * optionally the offset of one specific SHN_COMMON symbol inside it. + * + * SHN_COMMON symbols are tentative definitions of uninitialized global + * variables. The loader must allocate storage for them just like a static + * linker does: every common symbol is aligned to its st_value alignment + * requirement and occupies st_size bytes, packed one after another. + * + * @param symtab pointer to the symbol table + * @param nsym number of symbols in the table + * @param target symbol whose offset is wanted, or RT_NULL to get the total + * size of the common block + * @return offset of target in the common block, or the total size of the + * block when target is RT_NULL + */ +static rt_ubase_t dlmodule_common_block(Elf_Sym *symtab, rt_ubase_t nsym, + Elf_Sym *target) +{ + rt_ubase_t i, offset = 0; + + for (i = 0; i < nsym; i++) + { + if (symtab[i].st_shndx != SHN_COMMON) + { + continue; + } + + /* st_value of a SHN_COMMON symbol holds its alignment requirement */ + offset = RT_ALIGN(offset, symtab[i].st_value > 1 ? symtab[i].st_value : 1); + + if (target == &symtab[i]) + { + return offset; + } + + offset += symtab[i].st_size; + } + + return offset; +} + /** * @brief Load a relocatable file into memory. * @@ -319,8 +361,9 @@ rt_err_t dlmodule_load_shared_object(struct rt_dlmodule* module, void *module_pt */ rt_err_t dlmodule_load_relocated_object(struct rt_dlmodule* module, void *module_ptr) { - rt_ubase_t index, rodata_addr = 0, bss_addr = 0, data_addr = 0; - rt_ubase_t module_addr = 0, module_size = 0; + rt_ubase_t index, rodata_addr = 0, bss_addr = 0, data_addr = 0, common_addr = 0; + rt_ubase_t module_addr = 0, module_size = 0, common_align = 1; + rt_ubase_t *section_load_addr = RT_NULL; rt_uint8_t *ptr, *strtab, *shstrab; /* get the ELF image size */ @@ -349,6 +392,38 @@ rt_err_t dlmodule_load_relocated_object(struct rt_dlmodule* module, void *module } } + /* account for common symbols (uninitialized global variables) */ + for (index = 0; index < elf_module->e_shnum; index++) + { + Elf_Sym *symtab; + rt_ubase_t i, nsym; + + if (shdr[index].sh_type != SHT_SYMTAB) + { + continue; + } + + symtab = (Elf_Sym *)((rt_uint8_t *)elf_module + shdr[index].sh_offset); + nsym = (rt_ubase_t)(shdr[index].sh_size / sizeof(Elf_Sym)); + for (i = 0; i < nsym; i++) + { + if ((symtab[i].st_shndx == SHN_COMMON) && + (symtab[i].st_value > common_align)) + { + common_align = symtab[i].st_value; + } + } + module_size += RT_ALIGN(dlmodule_common_block(symtab, nsym, RT_NULL), + sizeof(rt_ubase_t)); + } + + /* The actual base is known only after allocation. Reserve the worst-case + * padding needed to align the common block after all sections are loaded. */ + if (common_align > 1) + { + module_size += common_align - 1; + } + /* no text, data and bss on image */ if (module_size == 0) return RT_NULL; @@ -363,6 +438,14 @@ rt_err_t dlmodule_load_relocated_object(struct rt_dlmodule* module, void *module } module->mem_size = module_size; + section_load_addr = rt_calloc(elf_module->e_shnum, + sizeof(*section_load_addr)); + if (section_load_addr == RT_NULL) + { + LOG_E("Module: allocate section address table failed.\n"); + return -RT_ENOMEM; + } + /* zero all space */ ptr = module->mem_space; rt_memset(ptr, 0, module_size); @@ -373,6 +456,7 @@ rt_err_t dlmodule_load_relocated_object(struct rt_dlmodule* module, void *module /* load text section */ if (IS_PROG(shdr[index]) && IS_AX(shdr[index])) { + section_load_addr[index] = (rt_ubase_t)ptr; rt_memcpy(ptr, (rt_uint8_t *)elf_module + shdr[index].sh_offset, shdr[index].sh_size); @@ -383,6 +467,7 @@ rt_err_t dlmodule_load_relocated_object(struct rt_dlmodule* module, void *module /* load rodata section */ if (IS_PROG(shdr[index]) && IS_ALLOC(shdr[index])) { + section_load_addr[index] = (rt_ubase_t)ptr; rt_memcpy(ptr, (rt_uint8_t *)elf_module + shdr[index].sh_offset, shdr[index].sh_size); @@ -395,6 +480,7 @@ rt_err_t dlmodule_load_relocated_object(struct rt_dlmodule* module, void *module /* load data section */ if (IS_PROG(shdr[index]) && IS_AW(shdr[index])) { + section_load_addr[index] = (rt_ubase_t)ptr; rt_memcpy(ptr, (rt_uint8_t *)elf_module + shdr[index].sh_offset, shdr[index].sh_size); @@ -407,12 +493,18 @@ rt_err_t dlmodule_load_relocated_object(struct rt_dlmodule* module, void *module /* load bss section */ if (IS_NOPROG(shdr[index]) && IS_AW(shdr[index])) { + section_load_addr[index] = (rt_ubase_t)ptr; rt_memset(ptr, 0, shdr[index].sh_size); bss_addr = (rt_ubase_t)ptr; LOG_D("load bss 0x%x, size %d", ptr, shdr[index].sh_size); + ptr += shdr[index].sh_size; } } + /* the common block (uninitialized global variables) starts right after + * the loaded sections; it was zeroed together with the module space */ + common_addr = RT_ALIGN((rt_ubase_t)ptr, common_align); + /* set module entry */ module->entry_addr = (rt_dlmodule_entry_func_t)((rt_uint8_t *)module->mem_space + elf_module->e_entry - module_addr); @@ -459,29 +551,48 @@ rt_err_t dlmodule_load_relocated_object(struct rt_dlmodule* module, void *module { Elf_Addr addr = 0; - if ((ELF_ST_TYPE(sym->st_info) == STT_SECTION) || - (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)) + if (sym->st_shndx == SHN_COMMON) { + /* relocate common symbol (uninitialized global variable): + * SHN_COMMON is not a real section index, so it must be + * handled before the section-name checks below */ + LOG_D("common symbol"); + addr = (Elf_Addr)(common_addr + dlmodule_common_block( + symtab, + (rt_ubase_t)(shdr[shdr[index].sh_link].sh_size / sizeof(Elf_Sym)), + sym)); + dlmodule_relocate(module, rel, addr); + } + else if ((ELF_ST_TYPE(sym->st_info) == STT_SECTION) || + (ELF_ST_TYPE(sym->st_info) == STT_OBJECT)) + { + Elf_Addr sec_sh_addr = shdr[sym->st_shndx].sh_addr; if (rt_strncmp((const char *)(shstrab + - shdr[sym->st_shndx].sh_name), ELF_RODATA, 8) == 0) + shdr[sym->st_shndx].sh_name), + ELF_RODATA, 8) == 0) { /* relocate rodata section */ LOG_D("rodata"); - addr = (Elf_Addr)(rodata_addr + sym->st_value); + addr = (Elf_Addr)(rodata_addr - sec_sh_addr + sym->st_value); } - else if (rt_strncmp((const char *) - (shstrab + shdr[sym->st_shndx].sh_name), ELF_BSS, 5) == 0) + else if (rt_strncmp((const char *)(shstrab + shdr[sym->st_shndx].sh_name), ELF_BSS, 5) == 0) { /* relocate bss section */ LOG_D("bss"); - addr = (Elf_Addr)bss_addr + sym->st_value; + addr = (Elf_Addr)(bss_addr - sec_sh_addr + sym->st_value); + } + else if (rt_strncmp((const char *)(shstrab + shdr[sym->st_shndx].sh_name), ".common", 7) == 0) + { + /* relocate common section (uninitialized globals) */ + LOG_D("common"); + addr = (Elf_Addr)(bss_addr - sec_sh_addr + sym->st_value); } else if (rt_strncmp((const char *)(shstrab + shdr[sym->st_shndx].sh_name), ELF_DATA, 6) == 0) { /* relocate data section */ LOG_D("data"); - addr = (Elf_Addr)data_addr + sym->st_value; + addr = (Elf_Addr)(data_addr - sec_sh_addr + sym->st_value); } if (addr != 0) dlmodule_relocate(module, rel, addr); @@ -533,5 +644,84 @@ rt_err_t dlmodule_load_relocated_object(struct rt_dlmodule* module, void *module } } + /* construct the module symbol table from .symtab so that lookups such as + * dlsym() can resolve the module's global functions */ + shstrab = (rt_uint8_t *)module_ptr + shdr[elf_module->e_shstrndx].sh_offset; + for (index = 0; index < elf_module->e_shnum; index++) + { + if ((shdr[index].sh_type == SHT_SYMTAB) && + (rt_strcmp((const char *)(shstrab + shdr[index].sh_name), ELF_SYMTAB) == 0)) + { + break; + } + } + + if (index != elf_module->e_shnum) + { + Elf_Sym *symtab; + rt_ubase_t i, count = 0; + + symtab = (Elf_Sym *)((rt_uint8_t *)module_ptr + shdr[index].sh_offset); + strtab = (rt_uint8_t *)module_ptr + shdr[shdr[index].sh_link].sh_offset; + + for (i = 0; i < shdr[index].sh_size / sizeof(Elf_Sym); i++) + { + if ((ELF_ST_BIND(symtab[i].st_info) == STB_GLOBAL) && + (ELF_ST_TYPE(symtab[i].st_info) == STT_FUNC) && + (symtab[i].st_shndx != SHN_UNDEF) && + (symtab[i].st_shndx < elf_module->e_shnum) && + (section_load_addr[symtab[i].st_shndx] != 0)) + { + count++; + } + } + + module->nsym = 0; + if (count > 0) + { + module->symtab = (struct rt_module_symtab *)rt_malloc( + count * sizeof(struct rt_module_symtab)); + if (module->symtab == RT_NULL) + { + LOG_E("Module: allocate symbol table failed.\n"); + rt_free(section_load_addr); + return -RT_ENOMEM; + } + + for (i = 0; i < shdr[index].sh_size / sizeof(Elf_Sym); i++) + { + rt_size_t length; + + if ((ELF_ST_BIND(symtab[i].st_info) != STB_GLOBAL) || + (ELF_ST_TYPE(symtab[i].st_info) != STT_FUNC) || + (symtab[i].st_shndx == SHN_UNDEF) || + (symtab[i].st_shndx >= elf_module->e_shnum) || + (section_load_addr[symtab[i].st_shndx] == 0)) + { + continue; + } + + length = rt_strlen((const char *)(strtab + symtab[i].st_name)) + 1; + + module->symtab[module->nsym].addr = + (void *)(section_load_addr[symtab[i].st_shndx] - + shdr[symtab[i].st_shndx].sh_addr + symtab[i].st_value); + module->symtab[module->nsym].name = rt_malloc(length); + if (module->symtab[module->nsym].name == RT_NULL) + { + LOG_E("Module: allocate symbol name failed.\n"); + rt_free(section_load_addr); + return -RT_ENOMEM; + } + rt_memset((void *)module->symtab[module->nsym].name, 0, length); + rt_memcpy((void *)module->symtab[module->nsym].name, + strtab + symtab[i].st_name, + length); + module->nsym++; + } + } + } + + rt_free(section_load_addr); return RT_EOK; } diff --git a/components/libc/posix/libdl/dlelf.h b/components/libc/posix/libdl/dlelf.h index 6b03271034f3..e5acaf7f5719 100644 --- a/components/libc/posix/libdl/dlelf.h +++ b/components/libc/posix/libdl/dlelf.h @@ -202,6 +202,8 @@ typedef struct #define STT_HIPROC 15 #define STN_UNDEF 0 /* undefined */ +#define SHN_UNDEF 0x0000 /* undefined section */ +#define SHN_COMMON 0xfff2 /* common symbols (uninitialized globals) */ #define ELF_ST_BIND(info) ((info) >> 4) #define ELF_ST_TYPE(info) ((info) & 0xf)