当我使用gdb调试用C编写的程序时,命令disassemble在代码存储器分段中显示代码及其地址。是否有可能在运行时知道这些内存地址?我正在使用Ubuntu OS。谢谢。
[edit]更具体地说,我将通过以下示例演示它。
#include <stdio.h>
int main(int argc,char *argv[]){
myfunction();
exit(0);
}
现在我希望在运行程序时在代码内存分段中有 myfunction()的地址。
答案 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
玩得开心!