c-位置无关可执行文件中的函数地址



我在C.中创建了一个小单元测试库

它的主要特点是,您不需要注册测试函数,它们被标识为测试函数,因为它们有一个预定义的前缀(test_(。

例如,如果你想创建一个测试函数,你可以写这样的东西:

int test_abc(void *t)
{
...
}

是的,就像围棋一样。

要找到测试功能,运行程序:

  1. 采用来自argv[0]的可执行文件的名称
  2. 解析ELF部分以找到符号表
  3. 从符号表中取所有名为test_*的函数
  4. 将来自符号表的地址视为函数指针
  5. 调用测试函数

对于PIE二进制文件,还有一个额外的步骤。为了找到测试函数的加载地址,我假设有一个适用于所有函数的公共偏移量。为了计算偏移量,我从符号表中读取的main的地址中减去main(运行时,函数指针(的地址。

上面描述的所有事情都很好:https://github.com/rodrigo-dc/testprefix

然而,据我所知,C99标准不允许函数指针算术。

假设我有符号表中的地址-是否有可靠的方法来获取函数的运行时地址(在PIE二进制文件的情况下(

我希望得到一些链接器变量、一些基地址或类似的东西

是否有可靠的方法来获取函数的运行时地址(在PIE二进制文件的情况下(?

是:请参阅此答案以及关于使用dladdr()的注释。

附言:注意,不允许在C++中取main的地址。

因为您有一个ELF可执行文件,这可能会排除"有趣的";可能具有分段或非线性、非连续地址空间的体系结构(例如Intel 8051、PIC等(。

因此,您[可能]可以使用您在main中描述的方法来获得实际地址。您只需要转换为char *uintptr_t类型,就可以使用字节偏移量/差异。


但是,您也可以通过创建描述符结构来创建指向各种函数的统一指针表,这些描述符结构放置在您选择的特殊链接器部分中,使用(例如(__attribute__((section("mysection"))

以下是一些代码,显示了我的意思:

#include <stdio.h>
typedef struct {
int (*test_func)(void *);           // pointer to test function
const char *test_name;              // name of the test
int test_retval;                    // test return value
// more data ...
int test_xtra;
} testctl_t;
// define a struct instance for a given test
#define ATTACH_TEST(_func) 
testctl_t _func##_ctl __attribute__((section("testctl"))) = { 
.test_func = _func, 
.test_name = #_func 
}
// advance to next struct (must be 16 byte aligned)
#define TESTNEXT(_test) 
(testctl_t *) (((char *) _test) + asiz)
int
test_abc(void *t)
{
printf("test_abc: hellon");
return 1;
}
ATTACH_TEST(test_abc);
int
test_def(void *t)
{
printf("test_def: hellon");
return 2;
}
ATTACH_TEST(test_def);
int
main(void)
{
// these are special symbols defined by the linker for our special linker
// section that denote the start/end of the section (similar to
// _etext/_edata)
extern testctl_t __start_testctl;
extern testctl_t __stop_testctl;
size_t rsiz = sizeof(testctl_t);
size_t asiz;
testctl_t *test;
// align the size to a 16 byte boundary
asiz = rsiz;
asiz += 15;
asiz /= 16;
asiz *= 16;
// show the struct sizes
printf("main: sizeof(testctl_t)=%zx/%zxn",rsiz,asiz);
// section start and stop symbol addresses
printf("main: start=%p stop=%pn",&__start_testctl,&__stop_testctl);
// cross check of expected pointer values
printf("main: test_abc=%p test_abc_ctl=%pn",test_abc,&test_abc_ctl);
printf("main: test_def=%p test_def_ctl=%pn",test_def,&test_def_ctl);
for (test = &__start_testctl;  test < &__stop_testctl;
test = TESTNEXT(test)) {
printf("n");
// show the address of our test descriptor struct and the pointer to
// the function
printf("main: test=%p test_func=%pn",test,test->test_func);
printf("main: calling %s ...n",test->test_name);
test->test_retval = test->test_func(test);
printf("main: return is %dn",test->test_retval);
}
return 0;
}

这是程序输出:

main: sizeof(testctl_t)=18/20
main: start=0x404040 stop=0x404078
main: test_abc=0x401146 test_abc_ctl=0x404040
main: test_def=0x401163 test_def_ctl=0x404060
main: test=0x404040 test_func=0x401146
main: calling test_abc ...
test_abc: hello
main: return is 1
main: test=0x404060 test_func=0x401163
main: calling test_def ...
test_def: hello
main: return is 2

最新更新