gpt4 book ai didi

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

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:13:15 24 4
gpt4 key购买 nike

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

上下文:我们希望在内存中有共享库的多个“实例”(例如协议(protocol)栈实现)以用于模拟目的(例如模拟具有多个主机/路由器的网络)。我们正在尝试的方法之一是只加载一次库,但通过创建和维护全局变量的“影子”集来模拟其他实例,并通过 memcpy()'ing 适当的方式在实例之间切换shadow set in/out 库的全局变量占用的内存区域。 (使用 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!

这意味着“保护变量”方法不起作用。

  • 也许我们应该遍历全局偏移表 (GOT) 并从那里收集全局变量的地址?但是,如果可能的话,似乎没有官方的方法可以做到这一点。

我们是否忽略了什么?如果需要,我很乐意澄清或发布我们的测试代码。

编辑:澄清一下,有问题的共享库是第 3 方库,我们不想修改其源代码,因此寻求上述通用解决方案。

EDIT2:作为进一步说明,以下代码概述了我希望能够执行的操作:

// x.c -- source for the shared library
#include <stdio.h>

int global_var = 10;

void bar() {
global_var++;
printf("global_var=%d\n", global_var);
}
// a.c -- main program
#include <stdlib.h>
#include <dlfcn.h>
#include <memory.h>

struct memrange {
void *ptr;
size_t size;
};

extern int global_var;
void bar();

struct memrange query_globals_address_range(const char *so_file)
{
struct memrange result;
// TODO what generic solution can we use here instead of the next two specific lines?
result.ptr = &global_var;
result.size = sizeof(int);
return result;
}

struct memrange g_range;


void *allocGlobals()
{
// allocate shadow set and initialize it with actual global vars
void *globals = malloc(g_range.size);
memcpy(globals, g_range.ptr, g_range.size);
return globals;
}

void callBar(void *globals) {
memcpy(g_range.ptr, globals, g_range.size); // overwrite globals from shadow set
bar();
memcpy(globals, g_range.ptr, g_range.size); // save changes into shadow set
}

int main(int argc, char *argv[])
{
g_range = query_globals_address_range("./libx.so");

// allocate two shadow sets of global vars
void *globals1 = allocGlobals();
void *globals2 = allocGlobals();

// call bar() in the library with a few times with each
callBar(globals1);
callBar(globals2);
callBar(globals2);
callBar(globals1);
callBar(globals1);

return 0;
}

构建+运行脚本:

#! /bin/sh
gcc -c -g -fPIC x.c -shared -o libx.so
gcc a.c -g -L. -lx -ldl -o prog
LD_LIBRARY_PATH=. ./prog

EDIT3:添加了dl_iterate_phdr()输出

最佳答案

共享库编译为Position-Independent Code .这意味着与可执行文件不同,地址不是固定的,而是在动态链接期间决定的。

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

关于c - 如何在运行时确定共享库中全局变量的地址范围?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58247922/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com