自学内容网 自学内容网

_RET_IP_ 和_THIS_IP_ 作用

在Linux内核中,有两个罕见的宏定义_RET_IP_ 和_THIS_IP_。但是这两个宏在内核代码中又时不时的出现,那么它们到底是什么含义呢?

1、宏定义

我们先看它们的宏定义

include./linux/kernel.h

#define _RET_IP_(unsigned long)__builtin_return_address(0)

#define _THIS_IP_  ({ __label__ __here; __here: (unsigned long)&&__here; })

我们先看_RET_IP_的含义:

#define _RET_IP_ (unsigned long)__builtin_return_address(0)

其中__builtin_return_address(0) 是gcc内建函数,返回函数的返回地址。所以_RET_IP_ 宏定义用于返回当前函数的返回地址(当前函数被调用处的地址)

在看看_THIS_IP_的含义:

#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; })

__lable__ 是gcc对c语言的扩展,用于局部标签,__label__ __here; 声明了一个局部标签__here。

什么是局部标签?

GCC allows you to declare local labels(局部标签) in any nested block scope.A local label is just like an ordinary label, but you can only reference it (with a goto statement, or by taking its address) within the block in which it is declared.

一个局部标签只是一个标识符:可以使用通常的goto语句进行跳转或者获取其地址,但是只能在其所属的域内内。

A local label declaration looks like this:(局部标签定义如下:)

__label__ label;

or

__label__ label1, label2, /* … */;

You can get the address of a label defined in the current function (or a containing function) with the unary operator ‘&&’.The value has type void *.

我们可以通过'&&'符号来获取局部标签的地址(获取变量的地址是'&'),地址类型为void *.

#define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; })

先定义了一个局部标签 __here,后面是__here标签的内容,和我们使用goto语句时的标签的用法是一样的,  __here: (unsigned long)&&__here; 返回局部标签__here所在位置的地址。所以_THIS_IP_返回当前位置的地址(PC指针的值)

内核中有个专门返回当前代码段的地址的宏定义:

#define current_text_addr() ({ __label__ _l; _l: &&_l;})

和_THIS_IP_的宏定义是一样的。

2、测试

下面我们写个简单的程序测试下

#include <stdio.h>
#include <stdlib.h>

#define _RET_IP_(unsigned long)__builtin_return_address(0)

#define _THIS_IP_  ({ __label__ __here; __here: (unsigned long)&&__here; })

void bar(void)
{
    /*This is bar (400551,40052a) */
    printf("This is bar (%x,%x) \012",_RET_IP_,_THIS_IP_);
    return ;
}

int main()
{
    bar();

    return 0;
}

上面程序输出结果是:This is bar (400551,40052a)

我们将上面程序反汇编结果如下:

0000000000400526 <bar>:

400526: 55 push %rbp

400527: 48 89 e5 mov %rsp,%rbp

40052a: ba 2a 05 40 00 mov $0x40052a,%edx

40052f: 48 8b 45 08 mov 0x8(%rbp),%rax

400533: 48 89 c6 mov %rax,%rsi

400536: bf e4 05 40 00 mov $0x4005e4,%edi

40053b: b8 00 00 00 00 mov $0x0,%eax

400540: e8 bb fe ff ff callq 400400 <printf@plt>

400545: 90 nop

400546: 5d pop %rbp

400547: c3 retq

0000000000400548 <main>:

400548: 55 push %rbp

400549: 48 89 e5 mov %rsp,%rbp

40054c: e8 d5 ff ff ff callq 400526 <bar>

400551: b8 00 00 00 00 mov $0x0,%eax

400556: 5d pop %rbp

400557: c3 retq

400558: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)

40055f: 00

_RET_IP_的打印结果是400551,但是调用bar函数的地址是40054c。由于流水线的原因,取指完成后PC=PC+4,所以_RET_IP_的值为400551.

_THIS_IP_的打印结果是40052a,该地址是printf打印中_THIS_IP_入栈时的地址值。

上面的打印结果和我们理解的结果是一致的。


原文地址:https://blog.csdn.net/qq_30896803/article/details/142520413

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!