__builtin_return_address函数

文章目录

  • 一、gcc 内置函数
  • 二、__builtin_return_address
    • 2.1 简介
    • 2.2 代码示例
  • 三、查看函数调用
  • 参考资料

一、gcc 内置函数

GCC 内置函数是指 GCC 编译器内置的一些函数,这些函数可以用于实现一些常用的操作,如数学运算、字符串处理、内存管理、调试等。这些函数与标准 C 库函数不同,它们通常具有更高的效率和更好的可移植性,因为它们是针对特定平台和编译器优化的。

GCC 内置函数可以分为以下几类:

(1)数学函数:包括常见的数学运算函数,如求平方根、求绝对值、求三角函数等。

(2)字符串函数:包括字符串操作函数,如字符串比较、字符串复制、字符串连接等。

(3)内存函数:包括内存操作函数,如内存填充、内存拷贝、内存分配、内存释放等。

(4)调试函数:包括用于调试和性能优化的函数,如获取函数调用栈、测量程序执行时间、预取数据到高速缓存等。

(5)其他函数:包括一些其他的常用函数,如比特位操作函数、字节序转换函数、原子操作函数等。

以下是一些常用的 GCC 内置函数:

__builtin_sqrt:求平方根函数。
__builtin_abs:求绝对值函数。
__builtin_strlen:求字符串长度函数。
__builtin_memset:内存填充函数。
__builtin_memcpy:内存拷贝函数。
__builtin_malloc:内存分配函数。
__builtin_free:内存释放函数。
__builtin_return_address:获取函数调用栈中的返回地址函数。
__builtin_prefetch:预取数据到高速缓存函数。
__builtin_bswap16、__builtin_bswap32、__builtin_bswap64:字节序转换函数。
__builtin_clz、__builtin_ctz、__builtin_popcount:比特位操作函数。

二、__builtin_return_address

2.1 简介

__builtin_return_address 是 GCC 内置函数之一,用于获取指定层数的函数调用栈中的返回地址。该函数接受一个整数参数 level,表示要获取的返回地址所在的函数调用栈层数。如果 level 等于 0,则返回当前函数调用的返回地址;如果 level 大于 0,则返回当前函数调用栈中第 level 层函数的返回地址。

比如:
0:返回当前函数的返回地址;
1:返回当前函数调用者的返回地址;
2:返回当前函数调用者的调用者的返回地址;

该函数的返回值类型是 void*,指向函数调用栈中的返回地址。需要注意的是,返回的地址是指向调用函数指令的地址,而不是指向函数返回值的地址。因此,如果需要获取函数返回值的地址,需要结合其他内置函数一起使用。

2.2 代码示例

(1)

#include 
#include 

void func1();
void func2();
void func3();

void func1() {
    printf("Function 1: %p\n", __builtin_return_address(0));
    printf("Function main: %p\n", __builtin_return_address(1));
    printf("\n");
    func2();
}

void func2() {
    printf("Function 2: %p\n", __builtin_return_address(0));
    printf("Function 1: %p\n", __builtin_return_address(1));
    printf("Function main: %p\n", __builtin_return_address(2));
    printf("\n");
    func3();
}

void func3() {
    printf("Function 3: %p\n", __builtin_return_address(0));
    printf("Function 2: %p\n", __builtin_return_address(1));
    printf("Function 1: %p\n", __builtin_return_address(2));
    printf("Function main: %p\n", __builtin_return_address(3));
}

int main() {
    printf("Function main: %p\n", __builtin_return_address(0));
    printf("\n");    
    func1();
    return 0;
}
# ./a.out
Function main: 0x7fdb571bd555

Function 1: 0x4006cf
Function main: 0x7fdb571bd555

Function 2: 0x4005c5
Function 1: 0x4006cf
Function main: 0x7fdb571bd555

Function 3: 0x40062c
Function 2: 0x4005c5
Function 1: 0x4006cf
Function main: 0x7fdb571bd555

使用 objdump 查看其汇编指令代码:
__builtin_return_address函数_第1张图片
__builtin_return_address函数_第2张图片
__builtin_return_address函数_第3张图片

以 func1 函数来说,调用 __builtin_return_address(1) ,就是获取 main函数调用 func1 函数的返回地址,该地址就是 main函数调用 func1 函数时的下一条指令的地址,main函数调用 call func1,先将调用 func1 函数时的下一条指令的地址压入栈中,然后在跳转到 func1 函数的地址。
call 指令等价于:

push address1(func1 next inst address)
jmp address2(func1 address)

address1和 address1不一样,address1是main函数调用 func1 函数时的下一条指令的地址,address2是func1 函数的地址。

(2)
接下来我们我们用 gdb 调试看一下,分别给上述的函数下断点:

(gdb) break main
Breakpoint 1 at 0x4006a5: file 2.c, line 31.
(gdb) rbreak ^func
Breakpoint 2 at 0x400581: file 2.c, line 9.
void func1();
Breakpoint 3 at 0x4005cb: file 2.c, line 16.
void func2();
Breakpoint 4 at 0x400632: file 2.c, line 24.
void func3();
(gdb) info break
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000004006a5 in main at 2.c:31
2       breakpoint     keep y   0x0000000000400581 in func1 at 2.c:9
3       breakpoint     keep y   0x00000000004005cb in func2 at 2.c:16
4       breakpoint     keep y   0x0000000000400632 in func3 at 2.c:24
(gdb) run
Starting program: a.out

__builtin_return_address函数_第4张图片
__builtin_return_address函数_第5张图片
可以看到和 __builtin_return_address 函数获取到的值是一样的。

(3)
接下来我们看看 func1 、func2 和 func3调用 ret 指令时寄存器的值,调用 ret 指令时,先把 栈帧中 pop出 上一个函数压入栈中的返回地址,然后跳转到该返回地址,ret 指令等价于:

pop address
jmp address

这两个地址都是一样的,比如 main函数,调用func1时,将调用 func1 函数时的下一条指令的地址 address 压入栈中,然后在跳转到 func1 函数的地址,然后再 func1 函数执行 ret 指令时,就 pop address ,然后跳转到 address ,回到 main 函数执行 func1 函数时的下一条指令的地址 address 的指令。

__builtin_return_address函数_第6张图片
使用 gdb在 func1返回前停住,反汇编用,查看对应寄存器下状态。0x0x4006c就是 main函数中调用func1时的下一条指令的地址。
对应的 func2、func3:
__builtin_return_address函数_第7张图片
__builtin_return_address函数_第8张图片

三、查看函数调用

我们还可以通过 __builtin_return_address 查看函数被哪个函数调用:
通过__builtin_return_address()获取函数地址后,再到到函数表中根据函数地址查找到对应的函数名即可:

info symbol address

在这里插入图片描述

参考资料

https://cloud.tencent.com/developer/article/1646414
https://blog.csdn.net/dinghuiyang/article/details/124245875
https://blog.csdn.net/zhaixuebuluo/article/details/86663338

你可能感兴趣的:(系统安全,linux,c语言,汇编)