最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【整理】__builtin_return_address(LEVEL)

工作和技术 crifan 8344浏览 0评论

看Linux内核代码过程中,看到一个有意思的:

mm/vmalloc.c中

struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)
{
return __get_vm_area_node(size, flags, VMALLOC_START, VMALLOC_END,
     -1, GFP_KERNEL, __builtin_return_address(0));
}
有个__builtin_return_address(0),到网上找了下,其具体解释如下:

——————————————————————————————————————————–

__builtin_return_address(LEVEL)

—This function returns the return address of the current function,or of one of its callers. The LEVEL argument is number of frames to scan up the call stack. A value of ‘0’ yields the return address of the current function,a value of ‘1’ yields the return address of the caller of the current function,and so forth.

此函数返回当前函数或其调用函数的返回地址。参数LEVEL决定调用栈的偏移位置:0则返回当前函数的返回地址,1返回调用当前函数的函数的返回地址;以此类推,2,3(只有四个常量值)。

The LEVEL argument must be a constant integer.

LEVEL参数必须是整形常量。

On some machine it may be impossible to determine the return address of any function other than the current one; in such cases,or when the top of the stack has been reached,this function will return ‘0’;

This function should only be used with a non-zero argument for debuging perposes.

from
http://gcc.gnu.org/ml/gcc/1999-07n/msg00892.html

——————————————————————————————————————————–

意思是,得到此程序的返回地址,return address,所以,自己去测试了一下:

在我的某个驱动中,加入如下测试代码:

static int easypoint_probe(struct i2c_client *client, const struct i2c_device_id *id)
{

printk("%sn", __func__);
printk("func[0]:%pn", __builtin_return_address(0));
printk("func[1]:%pn", __builtin_return_address(1));
printk("func[2]:%pn", __builtin_return_address(2));

}

最后测试结果为:

a.编译出现警告:

drivers/input/misc/easypoint.c: In function ‘easypoint_probe’:
drivers/input/misc/easypoint.c:723: warning: unsupported argument to ‘__builtin_return_address’
drivers/input/misc/easypoint.c:724: warning: unsupported argument to ‘__builtin_return_address’

b.程序运行结果为:

easypoint_probe
func[0]:c01940f0
func[1]:00000000
func[2]:00000000

对上述结果进行分析如下:

1。按照他人解释的,__builtin_return_address的参数为1,2等值,应该是支持的,

但是此处编译却出现警告:

warning: unsupported argument to ‘__builtin_return_address’

即不支持,所以,估计参数为0之外,都不支持吧。

注:当前gcc版本:

[crifan@linux-41lh linux-2.6.28.4]$arm-linux-gcc -v
Using built-in specs.
Target: arm-linux-uclibc
Configured with: /home/eric/buildroot/buildroot-2009.08/toolchain_build_arm/gcc-4.2.4/configure –prefix=/usr –build=i386-pc-linux-gnu –host=i386-pc-linux-gnu –target=arm-linux-uclibc –enable-languages=c,c++ –with-sysroot=/home/eric/buildroot/buildroot-2009.08/build_arm/staging_dir –with-build-time-tools=/home/eric/buildroot/buildroot-2009.08/build_arm/staging_dir/usr/arm-linux-uclibc/bin –disable-__cxa_atexit –enable-target-optspace –with-gnu-ld –disable-libssp –disable-tls –enable-shared –with-gmp=/home/eric/buildroot/buildroot-2009.08/toolchain_build_arm/gmp –with-mpfr=/home/eric/buildroot/buildroot-2009.08/toolchain_build_arm/mpfr –disable-nls –enable-threads –disable-multilib –with-float=soft –with-abi=apcs-gnu –with-arch=armv5te –with-tune=arm9tdmi
Thread model: posix
gcc version 4.2.4

2。不支持参数为1,2,那么运行结果只能得到0了。也就属于正常的了。

3。直到查看运行结果,才明白此宏的真正含义,是得到当前函数的 返回地址,而不是函数的地址。

即,运行结果func[0]:c01940f0
中的c01940f0,是easypoint_probe函数的返回(被别的函数调用后,此函数执行完毕,然后返回)那时候的地址,而不是函数easypoint_probe的地址。

函数easypoint_probe的地址,可以通过查看system.map得到:

c0190bd4 t easypoint_probe

而把func[0]:c01940f0中的c01940f0,再去和system.map中去比较,就可以看到真正的含义:
c01940f0的地址,介于下面两个函数之间:

c0194064 t i2c_device_probe
c0194100 t i2c_device_match

而此地址代表了easypoint_probe返回地址,意思就是说,easypoint_probe被i2c_device_probe调用了,在中间某个位置返回了,按照此意思回去查看内核源码,发现的确如此:

static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver = to_i2c_driver(dev->driver);
int status;

if (!driver->probe || !driver->id_table)
   return -ENODEV;
client->driver = driver;
if (!device_can_wakeup(&client->dev))
   device_init_wakeup(&client->dev,
      client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "proben");

status = driver->probe(client, i2c_match_id(driver->id_table, client));
if (status)
   client->driver = NULL;
return status;
}

其中 status = driver->probe(client, i2c_match_id(driver->id_table, client));
就是调用了easypoint_probe,而地址c01940f0,应该就对应着这句了:

if (status)
   client->driver = NULL;

【总结】

1。gcc默认不支持__builtin_return_address(LEVEL)的参数为非0。好像只支持参数为0。

2。__builtin_return_address(0)的含义是,得到当前函数返回地址,即此函数被别的函数调用,然后此函数执行完毕后,返回,所谓返回地址就是那时候的地址。注意不要像我一样,误以为函数调用者caller的函数地址了。。。

转载请注明:在路上 » 【整理】__builtin_return_address(LEVEL)

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (2)

  1. 今天也是看内核的时候搜到这个,分析的很好,谢谢。
    使用范围的问题,我个人认为是体系架构的问题。
    x86的函数调用通过栈,而且有栈帧是可以回溯的。而arm的好象是BLX了事,不知道建不建立栈帧的。具体他的ABI我也不熟悉,搞不清出。
    lhlzwj13年前 (2011-01-23)回复
  2. 兄弟,你的结论不太尽然啊。
    __builtin_return_address在用户空间可以使用非零参数,可以参考下面的
    http://hi.baidu.com/bestrongest/blog/item/f1b4766d7887adf8431694be.html
    newmalloc14年前 (2010-08-24)回复
82 queries in 0.168 seconds, using 22.13MB memory