在看linux源码的时候我们可能会经常碰见相关的东西,比如在dl_open的函数里边:
void* internal_function
_dl_open (const char *file, int mode, const void *caller)
{
struct dl_open_args args;
__rtld_lock_lock_recursive (GL(dl_load_lock));
args.file = file;
args.mode = mode;
args.caller = caller;
args.map = NULL;
dl_open_worker(&args);
__rtld_lock_unlock_recursive (GL(dl_load_lock));
}
这里的internal_function就是一个attribute的应用,其具体格式是:
#define internal_function __attribute__ ((regparm (3), stdcall))
attribute顾名思义,是说明函数(甚至是变量和结构体)的一些属性,这样给编译器提供更多的信息,我们在这里说明一些常用的attribute的用法。
gnu cc需要用 -Wall来激活这个选项
说明:即按照格式字符串来判断参数的合法性,如果不合法则编译器给出warning
具体格式:attribute(format(type, string_index, first_to_check))
解释: type填scanf或者printf,表示用scanf或者printf的方式进行检测,string_index表示格式字符串是函数的第几个参数,最后first_to_check表示需要检查的参数位于函数的那个位置
示例:
void myPrint(const char* format_string, ...)
__attribute__(format(printf, 1, 2));//format_string是格式化字符串,后面的...是需要检测的参数
//格式化字符串位于第一个,而需要检测的参数位于函数的第二个,所以是format(printf, 1, 2)
//即string_index = 1, first_to_check = 2
说明:表明该函数不需要返回给调用者,避免一些不必要的优化
具体格式:attribute((noreturn))
示例:
void myExit() __attribute__((noreturn));
说明:这个属性主要用于没有静态状态和副作用的函数,其返回值主要依赖于输入值,且只能用于带有数值类型的函数,不能用于带有指针类型参数的函数。
具体格式:attribute((const))
示例:
void intSqrt(int x)__attribute__((const))
//由于sqrt操作只依赖于x的输入,这样再调用相同的数值的时候会得到同样的输出,const就是针对这一类函数
//当重复调用同一个数值的时候,编译器会优化,使其只进行一次运算,而后来的调用都返回第一次的结果即可
说明:这个属性解释起来较为复杂,因为涉及到gcc的一个编译选项,-finstumat-function.添加了该参数,在编译的时候将会生成instrumentation调用,即在一个函数入口之后,和出口之前调用两个函数,分别是:
void __cyg_profile_func_enter(void *this_fn, void *call_site);
void __cyg_profile_func_exit(void *this_fn, void *call_site);
//profile函数
而不需要生成instrumentation调用的函数则使用该属性标明,典型的用法比如profile函数本身,高优先级的中断例程以及任何不能保证profiling正常调用的函数。
具体格式:attribute((no_instrument_function))
示例:
//test.c
#include
void __cyg_profile_func_enter(void *, void *)
__attribute__((no_instrument_function));
void __cyg_profile_func_exit(void *, void *)
__attribute__((no_instrument_function));
void __cyg_profile_func_exit(void *this_fn, void *call_site)
{
printf("this_func: %p call_addr: %p\n", this_fn, call_site);
}
void __cyg_profile_func_enter(void *this_fn, void *call_site)
{
printf("this_func: %p call_addr: %p\n", this_fn, call_site);
}
int main()
{
printf("hey, everybody!\n");
return 0;
}
编译命令:
gcc -o test test.c -finstrument-functions
输出结果:(我是64位系统)
this_func: 0x400645 call_addr: 0x7feebbbd8f45
hey, everybody!
this_func: 0x400645 call_addr: 0x7feebbbd8f45
说明:主要用于对函数的初始化等,如果声明了constructor属性,则会在main函数之前执行,如果声明了destructor属性则会在main函数执行之后,或者exit被调用后自动执行。
具体格式:
attribute((constructor))
attribute((destructor))
示例:
//test.c
#include
void constructor_func()
__attribute__((constructor));
void destructor_func()
__attribute__((destructor));
void constructor_func()
{
printf("I am a constructor :)\n");
}
void destructor_func()
{
printf("I am a destructor :)\n");
}
int main()
{
printf("I am main, I did nothing! :(\n");
return 0;
}
编译选项:
gcc -o test test.c
运行结果:
I am a constructor :)
I am main, I did nothing! :(
I am a destructor :)
说明:之前我们提到的所有设置,都是对函数进行设置,这里,除了对函数进行设置,也可以对变量和结构体进行属性设置。
常见变量属性:
int x[3] __attrubute__((aligned(16)))
即16字节对齐。(使对象占用更多空间)int x[3] __attribute__((packed))
(减少对象所占用空间) 在此我就不列出了,见更多变量属性。
说明:就像变量属性是对变量的属性声明,类型属性就是对类型的属性声明。主要是对struct和union进行属性声明,这里不再赘述,和第6点比较类似,更多类型属性见此。
__attrubute__((noreturn, format(printf, 1, 2)))
,当然也可以写多个attribute,如__attribute__((noreturn)) __attribute__((format(printf, 1, 2)))