开发者社区> 问答> 正文

如何在运行时确定共享库中全局变量的地址范围?

在运行时,是否保证加载的共享库中的全局变量会占用连续的内存区域?如果是这样,是否有可能找出该地址范围?

上下文:为了模拟目的(例如,模拟具有多个主机/路由器的网络),我们希望在内存中具有共享库(例如协议栈实现)的多个“实例”。我们正在尝试的方法之一是仅加载一次库,但通过创建和维护全局变量“影子”集来模拟其他实例,并通过memcpy()在占用的内存区域内/外设置适当的影子集在实例之间进行切换。通过库的全局变量。(另一种方法,例如dlmopen()用于多次加载库或在共享库中引入间接访问全局变量的方法也有其局限性和困难。)

我们尝试过的事情:

使用dl_iterate_phdr()查找共享库的数据段。产生的地址范围不太有用,因为(1)它并不指向包含实际全局变量的区域,而是指向从ELF文件加载的段(在只读存储器中),并且(2)它不仅包含全局变量,还有其他内部数据结构。

将C中的开始/结束保护变量添加到库代码中,并确保(通过链接程序脚本)将它们放置在.data共享库中该节的开始和结束处。(我们用验证了这一点objdump -t。)这个想法是在运行时,所有全局变量都将位于两个保护变量之间的地址范围内。但是,我们的观察是,内存中实际变量的相对顺序与共享对象中的地址所遵循的顺序完全不同。典型输出为:

$ objdump -t libx.so | grep '\.data'
0000000000601020 l    d  .data  0000000000000000              .data
0000000000601020 l     O .data  0000000000000000              __dso_handle
0000000000601038 l     O .data  0000000000000000              __TMC_END__
0000000000601030 g     O .data  0000000000000004              custom_data_end_marker
0000000000601028 g     O .data  0000000000000004              custom_data_begin_marker
0000000000601034 g       .data  0000000000000000              _edata
000000000060102c g     O .data  0000000000000004              global_var

$ ./prog
# output from dl_iterate_phdr()
name=./libx.so (7 segments)
    header  0: type=1 flags=5 start=0x7fab69fb0000 end=0x7fab69fb07ac size=1964
    header  1: type=1 flags=6 start=0x7fab6a1b0e08 end=0x7fab6a1b1038 size=560  <--- data segment
    header  2: type=2 flags=6 start=0x7fab6a1b0e18 end=0x7fab6a1b0fd8 size=448
    header  3: type=4 flags=4 start=0x7fab69fb01c8 end=0x7fab69fb01ec size=36
    header  4: type=1685382480 flags=4 start=0x7fab69fb0708 end=0x7fab69fb072c size=36
    header  5: type=1685382481 flags=6 start=0x7fab69bb0000 end=0x7fab69bb0000 size=0
    header  6: type=1685382482 flags=4 start=0x7fab6a1b0e08 end=0x7fab6a1b1000 size=504

# addresses obtained via dlsym() are consistent with the objdump output:
dlsym('custom_data_begin_marker') = 0x7fab6a1b1028
dlsym('custom_data_end_marker') =   0x7fab6a1b1030  <-- between the begin and end markers

# actual addresses: at completely different address range, AND in completely different order!
&custom_data_begin_marker = 0x55d613f8e018
&custom_data_end_marker =   0x55d613f8e010  <-- end marker precedes begin marker!
&global_var =               0x55d613f8e01c  <-- after both markers!

这意味着“保护变量”方法无效。

展开
收起
祖安文状元 2020-01-07 14:24:06 475 0
1 条回答
写回答
取消 提交回答
  • 共享库被编译为与位置无关的代码。这意味着与可执行文件不同,地址不是固定的,而是在动态链接期间确定的。

    从软件工程的角度来看,最好的方法是使用对象(结构)表示所有数据并避免使用全局变量(此类数据结构通常称为“上下文”)。然后,所有API函数都使用一个上下文参数,该参数允许您在同一过程中具有多个上下文。

    2020-01-07 14:24:12
    赞同 展开评论 打赏
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
15分钟打造你自己的小程序 立即下载
小程序 大世界 立即下载
《15分钟打造你自己的小程序》 立即下载