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
Changes from 1 commit
b96281f
5d6e93f
6880fad
180e625
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); | ||||||||||||||
|
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] COMMON 重定位需要写到目标段,不能直接使用段内偏移 ET_REL 的 复现代码: int common_value;
int *common_pointer = &common_value;
int function(void) { return 42; }编译命令: arm-none-eabi-gcc -marm -mcpu=arm926ej-s -fcommon -fno-pic -c common.c -o common.o
arm-none-eabi-ld -r common.o -o common.mo生成的 将本提交的加载函数和 ARM 重定位函数放入主机测试程序,模块内存固定在
请根据重定位段的
Contributor
Author
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. 感谢老师指出,已在提交 6880fad 中修复。现在根据重定位节的 sh_info 查找目标节实际加载地址,将 r_offset 换算为模块内偏移后再调用架构重定位函数。已补充 COMMON 跨节重定位测试,主机检查及 QEMU 验证均通过。 |
||||||||||||||
| } | ||||||||||||||
| 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); | ||||||||||||||
|
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] 多个 NOBITS 段仍被重定位到同一个 bss_addr 加载 NOBITS 段后虽然推进了 复现代码: int bss_value = 0;
int common_value;
int get_values(void) { return bss_value + common_value; }用下面的链接脚本把两类变量分别放入 SECTIONS
{
.text 0 : { *(.text*) }
.data 0 : { *(.data*) }
.bss 0 : { *(.bss*) }
.common 0 : { *(COMMON) }
}编译命令: arm-none-eabi-gcc -marm -mcpu=arm926ej-s -fcommon -c multibss.c -o multibss.o
arm-none-eabi-ld -r -d -T multibss.ld multibss.o -o multibss.mo使用本提交的加载函数和 ARM 重定位函数做主机测试,结果如下,偏移均相对于
两个变量仍共用地址,写入其中一个会覆盖另一个。请按
Contributor
Author
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. 感谢老师指出!已在提交 6880fad 中移除单个 bss_addr 的处理,统一通过 section_load_addr[st_shndx] + st_value 计算符号地址。已验证 .bss 和 .common 的地址独立,写入不同特征值后互不覆盖。 |
||||||||||||||
| } | ||||||||||||||
| 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] 检查符号表和符号名称的分配结果 新增的符号表数组分配没有检查返回值,后续会直接写入
Contributor
Author
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. 感谢老师提醒!这里确实考虑得不够周全。现已补充符号表和符号名称分配结果的检查,失败时返回 -RT_ENOMEM 并正确清理资源;同时,module->nsym 只统计完整初始化的条目,避免销毁时访问未初始化项。两类分配失败的注入测试均已通过,麻烦你再帮忙看一下! |
||||||||||||||
| 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] 从导出表中排除未定义函数 这里仅检查
Contributor
Author
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. 感谢指出!现已在计数和填表两个循环中同时排除 SHN_UNDEF,并补充段索引范围及已加载段检查,避免将外部函数引用误作为模块自身函数导出。相关测试均已通过,纯未定义符号场景下的导出数量为 0。若还有考虑不周之处,也麻烦您继续指出。 |
||||||||||||||
| { | ||||||||||||||
| 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 中已定义符号的
Contributor
Author
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. 感谢老师。现已记录各段的实际加载地址,并根据符号的 st_shndx、sh_addr 和段内偏移计算导出函数地址。多代码段测试中,两个函数的导出地址现在均能指向各自正确的位置,麻烦您再帮忙复核一下! |
||||||||||||||
| 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中预留相应填充空间;只对齐块大小不足以解决此问题。Uh oh!
There was an error while loading. Please reload this page.
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.
感谢老师的细致评审。已根据建议修正:现在会记录 COMMON 符号的最大对齐要求,在 module_size 中预留相应的填充空间,并对齐 common_addr,不再只对齐块内偏移。针对 4 字节和 16 字节对齐场景的测试均已通过,麻烦老师有空再帮忙确认一下!