看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的函数地址了。。。
使用范围的问题,我个人认为是体系架构的问题。
x86的函数调用通过栈,而且有栈帧是可以回溯的。而arm的好象是BLX了事,不知道建不建立栈帧的。具体他的ABI我也不熟悉,搞不清出。
__builtin_return_address在用户空间可以使用非零参数,可以参考下面的
http://hi.baidu.com/bestrongest/blog/item/f1b4766d7887adf8431694be.html