当我使用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机制类似)然后跳转到该功能.
如果在程序运行之前知道函数名称,只需使用
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)
要获得回溯,请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]