在运行时查找程序的代码地址?

时间:2009-07-01 15:34:38

标签: c

当我使用gdb调试用C编写的程序时,命令disassemble在代码存储器分段中显示代码及其地址。是否有可能在运行时知道这些内存地址?我正在使用Ubuntu OS。谢谢。

[edit]更具体地说,我将通过以下示例演示它。

#include <stdio.h>

int main(int argc,char *argv[]){
    myfunction();
    exit(0);
}

现在我希望在运行程序时在代码内存分段中有 myfunction()的地址。

4 个答案:

答案 0 :(得分:16)

以上答案非常复杂。如果函数引用是静态的,如上所述,地址就是指针上下文中符号名称的值:

void* myfunction_address = myfunction;

如果从共享库中动态获取函数,则从dlsym()(POSIX)或GetProcAddress()(窗口)返回的值同样是函数的地址。

请注意,上面的代码可能会生成一些编译器的警告,因为ISO C技术上禁止在代码和数据指针之间进行分配(某些体系结构将它们放在物理上不同的地址空间中)。

有些学生会指出返回的地址不是真的保证是函数的内存地址,它只是一个唯一的值,可以与其他函数指针进行比较,当被调用时,将控制转移到其所持有的指针的函数。显然,所有已知的编译器都使用分支目标地址来实现它。

最后,请注意函数的“地址”有点含糊不清。如果函数是动态加载的,或者是对导出符号的extern引用,那么你真正得到的通常是指向“PLT”中某些修正代码的指针(Unix / ELF术语,尽管windows上的PE / COFF机制类似)然后跳转到函数。

答案 1 :(得分:8)

如果您在程序运行之前知道函数名称,只需使用

void * addr = myfunction;

如果在运行时给出了函数名,我曾编写一个函数来使用bfd库动态查找符号地址。这是x86_64代码,您可以通过示例中的find_symbol(“a.out”,“myfunction”)获取地址。

#include <bfd.h>
#include <stdio.h>
#include <stdlib.h>
#include <type.h>
#include <string.h>

long find_symbol(char *filename, char *symname)
{
    bfd *ibfd;
    asymbol **symtab;
    long nsize, nsyms, i;
    symbol_info syminfo;
    char **matching;

    bfd_init();
    ibfd = bfd_openr(filename, NULL);

    if (ibfd == NULL) {
        printf("bfd_openr error\n");
    }

    if (!bfd_check_format_matches(ibfd, bfd_object, &matching)) {
        printf("format_matches\n");
    }

    nsize = bfd_get_symtab_upper_bound (ibfd);
    symtab = malloc(nsize);
    nsyms = bfd_canonicalize_symtab(ibfd, symtab);

    for (i = 0; i < nsyms; i++) {
        if (strcmp(symtab[i]->name, symname) == 0) {
            bfd_symbol_info(symtab[i], &syminfo);
            return (long) syminfo.value;
        }
    }

    bfd_close(ibfd);
    printf("cannot find symbol\n");
}

答案 2 :(得分:5)

要获得回溯,请使用execinfo.h记录的#include <execinfo.h> #include <stdio.h> #include <unistd.h> void trace_pom() { const int sz = 15; void *buf[sz]; // get at most sz entries int n = backtrace(buf, sz); // output them right to stderr backtrace_symbols_fd(buf, n, fileno(stderr)); // but if you want to output the strings yourself // you may use char ** backtrace_symbols (void *const *buffer, int size) write(fileno(stderr), "\n", 1); } void TransferFunds(int n); void DepositMoney(int n) { if (n <= 0) trace_pom(); else TransferFunds(n-1); } void TransferFunds(int n) { DepositMoney(n); } int main() { DepositMoney(3); return 0; }

例如:

{{1}}

编译

gcc a.c -o a -g -Wall -Werror -rdynamic

根据上述网站:

  

目前,仅在使用ELF的系统上获取函数名称和偏移量   程序和库的二进制格式。在其他系统上,只返回十六进制   地址将出现。此外,您可能需要将其他标志传递给链接器   使函数名可用于程序。 (例如,在使用GNU的系统上   ld,你必须通过(-rdynamic。)

输出

./a(trace_pom+0xc9)[0x80487fd]
./a(DepositMoney+0x11)[0x8048862]
./a(TransferFunds+0x11)[0x8048885]
./a(DepositMoney+0x21)[0x8048872]
./a(TransferFunds+0x11)[0x8048885]
./a(DepositMoney+0x21)[0x8048872]
./a(TransferFunds+0x11)[0x8048885]
./a(DepositMoney+0x21)[0x8048872]
./a(main+0x1d)[0x80488a4]
/lib/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7e16775]
./a[0x80486a1]

答案 3 :(得分:3)

关于答案中的评论(获取指令的地址),你可以使用这个非常难看的技巧

#include <setjmp.h> 

void function() {
    printf("in function\n");
    printf("%d\n",__LINE__);
    printf("exiting function\n");

}

int main() {
    jmp_buf env;
    int i;

    printf("in main\n");
    printf("%d\n",__LINE__);
    printf("calling function\n");
    setjmp(env);
    for (i=0; i < 18; ++i) {
        printf("%p\n",env[i]);
    }    
    function();
    printf("in main again\n");
    printf("%d\n",__LINE__);

}

它应该是env [12](eip),但要小心,因为它看起来与机器有关,所以三重检查我的话。这是输出

in main
13
calling function
0xbfff037f
0x0
0x1f80
0x1dcb
0x4
0x8fe2f50c
0x0
0x0
0xbffff2a8
0xbffff240
0x1f
0x292
0x1e09
0x17
0x8fe0001f
0x1f
0x0
0x37
in function
4
exiting function
in main again
37

玩得开心!