gpt4 book ai didi

c - 从名称 [.debug_info ??] 获取函数地址

转载 作者:太空宇宙 更新时间:2023-11-04 04:58:00 25 4
gpt4 key购买 nike

我正在尝试编写一个小型调试实用程序,为此我需要获取给定名称的函数/全局变量地址。这是内置的调试实用程序,这意味着调试实用程序将从要调试的代码中运行,或者说我无法解析可执行文件。

现在有一种众所周知的方法可以做到这一点吗?我的计划是将 .debug_* 部分加载到内存中 [我计划在 ld 脚本中通过这样的廉价技巧来完成]

.数据{ *(。数据) __sym_start = .; (调试_); __sym_end = .;

现在我必须解析该部分以获取我需要的信息,但我不确定这是否可行或是否存在问题 - 这只是理论。但似乎工作量太大了 :-) 有没有简单的方法。或者,如果有人可以预先告诉我为什么我的方案行不通,那也会有帮助。

提前致谢,亚历克斯。

最佳答案

如果您在带有 dlopen(3) 的系统下运行和 dlsym(3) (比如 Linux)你应该能够:

char thing_string[] = "thing_you_want_to_look_up";
void * handle = dlopen(NULL, RTLD_LAZY | RTLD_NOLOAD);
// you could do RTLD_NOW as well. shouldn't matter
if (!handle) {
fprintf(stderr, "Dynamic linking on main module : %s\n", dlerror() );
exit(1);
}

void * addr = dlsym(handle, thing_string);
fprintf(stderr, "%s is at %p\n", thing_string, addr);

我不知道对其他系统执行此操作的最佳方法,而且这可能不适用于静态变量和函数。如果您有兴趣使用它们,C++ 符号名称将被破坏。

要扩展它以适用于共享库,您可能可以从 /proc/self/maps 中获取当前加载的库的名称。然后将库文件名传递给 dlopen ,尽管如果库已被重命名或删除,这可能会失败。

可能还有其他几种更好的方法来解决这个问题。

编辑 使用 dlopen

/* name_addr.h */
struct name_addr {
const char * sym_name;
const void * sym_addr;
};
typedef struct name_addr name_addr_t;
void * sym_lookup(cost char * name);
extern const name_addr_t name_addr_table;
extern const unsigned name_addr_table_size;

/* name_addr_table.c */
#include "name_addr.h"

#define PREMEMBER( X ) extern const void * X
#define REMEMBER( X ) { .sym_name = #X , .sym_addr = (void *) X }

PREMEMBER(strcmp);
PREMEMBER(printf);
PREMEMBER(main);
PREMEMBER(memcmp);
PREMEMBER(bsearch);
PREMEMBER(sym_lookup);
/* ... */

const name_addr_t name_addr_table[] =
{
/* You could do a #include here that included the list, which would allow you
* to have an empty list by default without regenerating the entire file, as
* long as your compiler only warns about missing include targets.
*/
REMEMBER(strcmp),
REMEMBER(printf),
REMEMBER(main),
REMEMBER(memcmp),
REMEMBER(bsearch),
REMEMBER(sym_lookup);
/* ... */
};
const unsigned name_addr_table_size = sizeof(name_addr_table)/sizeof(name_addr_t);

/* name_addr_code.c */
#include "name_addr.h"
#include <string.h>

void * sym_lookup(cost char * name) {
unsigned to_go = name_addr_table_size;
const name_addr_t *na = name_addr_table;
while(to_to) {
if ( !strcmp(name, na->sym_name) ) {
return na->sym_addr;
}
na++;
to_do--;
}
/* set errno here if you are using errno */
return NULL; /* Or some other illegal value */
}

如果您这样做,链接器将在所有内容都布置好后负责为您填写地址。如果您在表中列出的所有符号都包含头文件,那么在编译表文件时不会收到警告,但将它们全部设为 extern void * 会容易得多。并让编译器警告您所有这些(它可能会,但不一定)。

您可能还想按名称对符号进行排序,这样您就可以使用列表的二分搜索而不是遍历它。

你应该注意,如果你在表中的成员没有被程序以其他方式引用(比如你在表中有一个 sqrt 的条目,但没有调用它)链接器将需要 (需要)将这些功能链接到您的图像中。这会使它爆炸。

此外,如果您正在利用具有此表的全局优化可能会降低这些效率,因为编译器会认为列出的所有函数都可以通过此列表中的指针访问并且它看不到所有调用点数。

将静态函数放在这个列表中并不是直截了当的。您可以通过将表更改为动态表并在运行时从每个模块中的函数执行此操作,或者可能通过在表所在的对象文件中生成一个新部分来完成此操作。如果您使用的是 gcc:

#define SECTION_REMEMBER(X) \
static const name_addr_t _name_addr##X = \
{.sym_name= #X , .sym_addr = (void *) X } \
__attribute__(section("sym_lookup_table" ) )

然后将这些列表添加到每个 .c 文件的末尾,其中包含您想从该文件中记住的所有符号。这将需要链接器工作,以便链接器知道如何处理这些成员,但是您可以通过查看它所在部分的开头和结尾来遍历列表(我不知道该怎么做这个,但我知道它可以做到并且不太困难)。不过,这将使排序列表变得更加困难。另外,我不完全确定初始化 .sym_name到字符串文字的地址不会导致将字符串塞入此部分,但我认为不会。如果它这样做了,那么这将破坏一切。

您仍然可以使用 objdump 来获取对象文件(可能是 elf)包含的符号列表,然后将其过滤为您感兴趣的符号,然后重新生成列出表成员的表文件。

关于c - 从名称 [.debug_info ??] 获取函数地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3039488/

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