Repository navigation
[dlmodule] fix: uninitialized globals in relocatable modules #11915
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,7 +361,7 @@ 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 index, rodata_addr = 0, bss_addr = 0, data_addr = 0, common_addr = 0; | ||
| rt_ubase_t module_addr = 0, module_size = 0; | ||
| rt_uint8_t *ptr, *strtab, *shstrab; | ||
|
|
||
|
|
@@ -349,6 +391,23 @@ 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 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)); | ||
| module_size += RT_ALIGN(dlmodule_common_block(symtab, nsym, RT_NULL), | ||
| sizeof(rt_ubase_t)); | ||
| } | ||
|
|
||
| /* no text, data and bss on image */ | ||
| if (module_size == 0) return RT_NULL; | ||
|
|
||
|
|
@@ -410,9 +469,14 @@ rt_err_t dlmodule_load_relocated_object(struct rt_dlmodule* module, void *module | |
| 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_ubase_t)ptr; | ||
|
|
||
| /* 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 +523,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 +616,59 @@ 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)) | ||
| { | ||
| count++; | ||
| } | ||
| } | ||
|
|
||
| module->symtab = (struct rt_module_symtab *)rt_malloc(count * sizeof(struct rt_module_symtab)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] 检查符号表和符号名称的分配结果 新增的符号表数组分配没有检查返回值,后续会直接写入 |
||
| module->nsym = count; | ||
| for (i = 0, count = 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)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] 从导出表中排除未定义函数 这里仅检查 |
||
| { | ||
| continue; | ||
| } | ||
|
|
||
| length = rt_strlen((const char *)(strtab + symtab[i].st_name)) + 1; | ||
|
|
||
| module->symtab[count].addr = | ||
| (void *)((rt_uint8_t *)module->mem_space - module_addr + symtab[i].st_value); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] 根据符号所属段计算导出函数地址 ET_REL 中已定义符号的 |
||
| module->symtab[count].name = rt_malloc(length); | ||
| rt_memset((void *)module->symtab[count].name, 0, length); | ||
| rt_memcpy((void *)module->symtab[count].name, | ||
| strtab + symtab[i].st_name, | ||
| length); | ||
| count++; | ||
| } | ||
| } | ||
|
|
||
| return RT_EOK; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1] 对齐 COMMON 块基址,并为填充预留空间
这里直接将已加载段的结束地址作为
common_addr,但dlmodule_common_block()只对齐相对于块起点的偏移,不能保证最终符号地址满足st_value的对齐要求。使用当前加载函数复现:8 字节 text 后接 1 字节 data,要求 4 字节对齐的 COMMON 变量被放在mem_space + 9,最终地址模 4 为 1。这会让要求对齐的访问在目标平台上产生异常。请按 COMMON 符号的最大对齐要求对齐块基址,并在module_size中预留相应填充空间;只对齐块大小不足以解决此问题。