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 是 GCC 内置函数之一,用于获取指定层数的函数调用栈中的返回地址。该函数接受一个整数参数 level,表示要获取的返回地址所在的函数调用栈层数。如果 level 等于 0,则返回当前函数调用的返回地址;如果 level 大于 0,则返回当前函数调用栈中第 level 层函数的返回地址。
比如:
0:返回当前函数的返回地址;
1:返回当前函数调用者的返回地址;
2:返回当前函数调用者的调用者的返回地址;
该函数的返回值类型是 void*,指向函数调用栈中的返回地址。需要注意的是,返回的地址是指向调用函数指令的地址,而不是指向函数返回值的地址。因此,如果需要获取函数返回值的地址,需要结合其他内置函数一起使用。
(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
以 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 函数获取到的值是一样的。
(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 的指令。
使用 gdb在 func1返回前停住,反汇编用,查看对应寄存器下状态。0x0x4006c就是 main函数中调用func1时的下一条指令的地址。
对应的 func2、func3:
我们还可以通过 __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