背景:
程序运行异常时发现使用的A全局变量中的成员值被修改,经定位是B全局变量存在地址越界操作;
为了复现验证,搭建了简易测试程序后,竟然无法复现(A全局成员被修改)!!!。
后来一步步回退,当把全局变量名也保持一致时,问题可复现。
简易测试程序:
struct rte_pci_device testa[1];
struct mem_info mem_infos[PAGE_MAX][1];
#if 1
struct rte_pci_device* test_fun(struct rte_pci_device *p_dev,const char *dirname, uint16_t domain, uint8_t bus,
uint8_t devid, uint8_t function)
{
char filename[PATH_MAX];
unsigned long tmp;
struct rte_pci_device *dev = p_dev;
...
...
tmp = 0x1086;
printf("bp2:vaddr=%p\n",mem_infos[0][0].vaddr);
dev->vendor_id = (uint16_t)tmp;
printf("bp3:vaddr=%p\n",mem_infos[0][0].vaddr);
return dev;
}
int pci_scan(void)
{
char dirname[PATH_MAX];
int index = 0;
uint16_t domain;
uint8_t bus, devid, function;
...
...
...
while(index<=1)
{
if(test_fun(testa+index,dirname, domain, bus, devid, function))
{
index++;
}
}
return 0;
}
过程描述:
rte_pci_device结构:对齐后有128字节
1.同一个测试程序,只是将全局变量testa恢复成原程序名chiper_pci后就能复现。
2. 查看程序地址信息:
A:测试程序将全局变量命名为testa时;
0000000000410c38 w .data 0000000000000000 data_start
0000000000410c48 g .bss 0000000000000000 __bss_start__
0000000000410d58 g .bss 0000000000000000 _bss_end__
0000000000410c48 g .data 0000000000000000 _edata
0000000000400844 g F .text 000000000000002c uio_init
0000000000400960 g F .fini 0000000000000000 _fini
0000000000410d58 g .bss 0000000000000000 __bss_end__
0000000000410c50 g O .bss 0000000000000028 testa
0000000000400520 F *UND* 0000000000000000 __libc_start_main@@GLIBC_2.17
0000000000400530 F *UND* 0000000000000000 memset@@GLIBC_2.17
0000000000410c78 g O .bss 0000000000000004 sum_fpga
0000000000410c38 g .data 0000000000000000 __data_start
0000000000000000 w *UND* 0000000000000000 __gmon_start__
0000000000410c40 g O .data 0000000000000000 .hidden __dso_handle
0000000000400550 F *UND* 0000000000000000 abort@@GLIBC_2.17
0000000000400970 g O .rodata 0000000000000004 _IO_stdin_used
0000000000400560 F *UND* 0000000000000000 puts@@GLIBC_2.17
0000000000410c80 g O .bss 00000000000000d8 mem_infos
00000000004008e0 g F .text 0000000000000078 __libc_csu_init
0000000000410d58 g .bss 0000000000000000 _end
0000000000400580 g F .text 0000000000000000 _start
0000000000410d58 g .bss 0000000000000000 __end__
0000000000410c48 g .bss 0000000000000000 __bss_start
0000000000400870 g F .text 000000000000006c main
0000000000000000 w *UND* 0000000000000000 _Jv_RegisterClasses
0000000000410c48 g O .data 0000000000000000 .hidden __TMC_END__
0000000000000000 w *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000400570 F *UND* 0000000000000000 printf@@GLIBC_2.17
00000000004004e0 g F .init 0000000000000000 _init
B:测试程序将全局变量命名为chiper_pci时;
0000000000410c38 w .data 0000000000000000 data_start
0000000000410c48 g .bss 0000000000000000 __bss_start__
0000000000410d58 g .bss 0000000000000000 _bss_end__
0000000000410c48 g .data 0000000000000000 _edata
0000000000400844 g F .text 000000000000002c uio_init
0000000000400960 g F .fini 0000000000000000 _fini
0000000000410d58 g .bss 0000000000000000 __bss_end__
0000000000400520 F *UND* 0000000000000000 __libc_start_main@@GLIBC_2.17
0000000000400530 F *UND* 0000000000000000 memset@@GLIBC_2.17
0000000000410c50 g O .bss 0000000000000004 sum_fpga
0000000000410c38 g .data 0000000000000000 __data_start
0000000000000000 w *UND* 0000000000000000 __gmon_start__
0000000000410c40 g O .data 0000000000000000 .hidden __dso_handle
0000000000400550 F *UND* 0000000000000000 abort@@GLIBC_2.17
0000000000400970 g O .rodata 0000000000000004 _IO_stdin_used
0000000000400560 F *UND* 0000000000000000 puts@@GLIBC_2.17
0000000000410c80 g O .bss 00000000000000d8 mem_infos
00000000004008e0 g F .text 0000000000000078 __libc_csu_init
0000000000410c58 g O .bss 0000000000000028 chiper_pci
0000000000410d58 g .bss 0000000000000000 _end
0000000000400580 g F .text 0000000000000000 _start
0000000000410d58 g .bss 0000000000000000 __end__
0000000000410c48 g .bss 0000000000000000 __bss_start
0000000000400870 g F .text 000000000000006c main
0000000000000000 w *UND* 0000000000000000 _Jv_RegisterClasses
0000000000410c48 g O .data 0000000000000000 .hidden __TMC_END__
0000000000000000 w *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000400570 F *UND* 0000000000000000 printf@@GLIBC_2.17
00000000004004e0 g F .init 0000000000000000 _init
验证结果:
同一个程序,全局的不同命名,编译器在静态存储区分配的地址是不相同的。
从而导致命名chiper_pci时,地址分配在比mem_infos低位,以至于在对chiper_pci越界操作时,会对mem_infos造成影响。
ps:测试环境是arm平台版本,后续基于x86平台编译程序无法复现,原因:不通平台编译出的程序对应的地址分支差异很大,x86版本两个全局变量跨度非常远,逻辑分析只有增加循环次数,才会有影响(不排除其他版本/平台编译器会直接像A情况一样,直接分配到高地址位)
原因分析:
在Linux下,编译器通常使用一种名为Name Mangling的技术来实现函数重载和命名空间等功能。在使用Name Mangling时,编译器会把函数名或变量名转换成编译器可以识别的形式,以保证符号在链接期间能够正确地解析和匹配。
因此,在对全局变量进行NameMangling时,编译器可能会根据变量名、作用域等多种因素生成不同的内部名称,并在编译器内部维护一个符号表,把变量名和对应的内部名称进行映射。所以即使全局变量的名字不同,它们在程序运行时可能会被分配到不同的内存地址。