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

wak*_*dan 7 c

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

[编辑]更具体地说,我将通过以下示例演示它.

#include <stdio.h>

int main(int argc,char *argv[]){
    myfunction();
    exit(0);
}
Run Code Online (Sandbox Code Playgroud)

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

And*_*oss 16

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

void* myfunction_address = myfunction;
Run Code Online (Sandbox Code Playgroud)

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

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

而一些学究会指出,返回没有地址确实保证是函数的内存地址,它只是一个独特的价值,可以与其他函数指针和行为,调用时,平等进行比较,控制转移到它所持有的指针的函数.显然,所有已知的编译器都使用分支目标地址来实现它.

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

  • 那里没有运气.编译器可以自由重新编码和优化代码,因此没有一个内存区域对应于任何给定的行或表达式.调试器可以从符号表中重构这个并在可执行文件中调试信息,这是非常合理的工作,但不幸的是,你正在进入一些我不熟悉的深层伏都教. (2认同)

Zel*_*luX 8

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

void * addr = myfunction;
Run Code Online (Sandbox Code Playgroud)

如果函数名是在运行时给出的,我曾经写过一个函数来使用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");
}
Run Code Online (Sandbox Code Playgroud)


Adr*_*iuk 5

要获得回溯,请execinfo.h按照GNU libc手册中的说明使用.

例如:

#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;
}
Run Code Online (Sandbox Code Playgroud)

编译

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]