gcc 中 weak 属性扩展的使用 __atrribute__((xxx))

The weak attribute causes the declaration to be emitted as a weak symbol rather than a global. This is primarily useful in defining library functions which can be overridden in user code, though it can also be used with non-function declarations. Weak symbols are supported for ELF targets, and also for a.out targets when using the GNU assembler and linker.

使用__attribute__((weak)) 的场景

A,B两个模块,A模块调用了B模块中的函数,但是不确定B模块是否提供了函数,但是又不得不调用,这个时候在A模块中再声明一个弱符号函数,即用weak。如果外部提供了就调用外部的,如果没提供调用声明的 weak 函数。在工作当中就是保证在没有链接某个库时能编译并链接通过

使用 weak 声明不存在的函数,可以编译并链接通过,但这个声明的 weak 函数为 NULL,也就是说此声明的函数名=NULL。函数运行时,要注意判断这种情况,否则 Segmentation fault。


attribute((weak))的作用

使模块的函数转换为弱符号类型,连接器发现同时存在弱符号和强符号,优先选择强符号,如果发现不存在强符号。只存在弱符号,则选择弱符号。

看如下例子:

func.c 中定义了函数 void func()。 我们编译时并没有编译链接 func.c 。如果不使用  weak 的话,链接时就会报错。使用了 weak 链接时并没有报错。也就说此时  func() 函数不存在。

// main.c
#include <stdio.h>


/*__attribute__((weak))*/ int a;
/*__attribute__((weak))*/ int b = 2;

extern __attribute__((weak)) void func();

int main()
{
    printf("main.a = %d\n", a);
    printf("main.b = %d\n", b);
    printf("func address %p\n", func);

    func();

    return 0;
}

tester:~$ gcc -o main main.c        -------- 没有编译链接 func.c
tester:~$                                        ------- 编译链接并没报错

运行

tester:~$ ./main
main.a = 0
main.b = 2
func address (nil)                      --- func = NULL
Segmentation fault
tester:~$ 

所以避免出现异常(Segmentation fault) :使用 weak 修饰的函数应先判断一下是否定义。

int main() 
{ 
	if (func) 
		func(); 
	else 
		printf("no func\n"); 
	
	return 0;
}

Usage of weak function symbol

After some research, I think weak function symbol has two usages, the first usage is to avoid the problem of multiple strong symbol conflicts, and the second usage is to change the symbol in link .so to NULL.

The first usage is to apply the weak symbol to where the function is defined, this usage is to avoid strong symbol conflict. Because the binary generated by all .o links in the same code can only have one strong symbol with the same name, if there are two symbols with the same name, the remaining symbols need to be set to weak functions.

Another usage is weak sumbol used in where the function is declared, so that when weak symbol is applied to function, the symbol will be regarded as NULL by program,

And use readelf -s to see that foo is weak and UND:
     5: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND func

tester:~$ readelf -s main

Symbol table '.dynsym' contains 8 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _[...]@GLIBC_2.34 (2)
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterT[...]
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND [...]@GLIBC_2.2.5 (3)
     4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     5: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND func
     6: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMC[...]
     7: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND [...]@GLIBC_2.2.5 (3)

[GNU][C/C++] Weak symbol: __atrribute__((weak)) - steveyang

Usage 1: Change the function to a weak symbol. If there are functions with the same name appearing in multiple .o's, they can be overwritten by strong symbols.

Usage 2: Change the function to a dynamic decision. If there is a function definition, there will be a normal function pointer. If there is no function definition, it will be a NULL function pointer.

总结

weak 函数用于定义变量或者函数弱函数一般用于多个模块间的交互接口

int __attribute__((weak)) test_lib_a(int a, int b)
{
        printf(
"weak %s\n", __FUNCTION__);
       
return a + b;
}

1、weak 属性只会在静态库(.o .a )中生效动态库(.so)中不会生效

2、weak 属性只对链接器生效,即链接器在允许两个同名符号表(一个强符号、一个或多个弱符号),编译器不允许在同一个文件中存在两个同名的符号。

3、链接器优先链接定义为非 weak 的函数或变量,如果找不到再连链接 weak 函数或者变量,若都没有,静态链接则链接时报错,若动态链接时则系统无法启动。

4、对于动态库,weak 属性毫无作用,调用哪个符号,取决于库链接顺序。

5、weak 在C99中没有,属于编译器外扩的,gcc中使用的是 __attribute__((weak)。

6、库链接顺序:从左到右,越基础的越靠右。(即在左边找到了需要符号,则会用靠近左边库中的符号)。

armcc 与 gcc

在 ARM 编译器(armcc)中,支持和 GCC 相同的关键字 __attribute__,使用方式也基本相同

__attribute__((weak)) function attribute

Functions defined with __attribute__((weak)) export their symbols weakly.

Functions declared with __attribute__((weak)) and then defined without __attribute__((weak)) behave as weak functions. This is not the same behavior as the __weak keyword.

除此之外,ARM 编译器(armcc)还扩展了一个关键字 __weak,例如:__weak void f(void); 或者 __weak int i;。ARM 的汇编器(armasm)以另一种方式 [WEAK] 支持该特性。

「注意:」在许多源码中,经常通过宏定义的形式来定义关键字,例如 上面linux 中的 __weak 就是 宏定义的 __attribute__((weak))

「注意:用 __weak 声明然后不使用 __weak 定义的函数的行为相当于非弱函数。」 这与 _attribute__((weak)) 关键字不同!

在 GCC 中,被 __attribute__((weak)) 修饰的符号,我们称之为 「弱符号(Weak Symbol)」。例如:弱函数、弱变量;没有 __attribute__((weak)) 修饰的符号被称为「强符号」。在 ARM 中,没有弱符号和强符号这种叫法,只有个「弱引用(Weak References)」 和 「非弱引用(non-weak reference )」 、 「弱定义(Weak definitions)」 和 「非弱定义(non-weak definition)」 的介绍章节。

附加

GNU C 编译器增加了一个 __attribute__ 关键字用来声明一个函数、变量或类型的特殊属性。声明这些属性主要用途就是指导编译程序进行特定方面的优化或代码检查。

__attrabute__ 的用法非常简单,当我们定义一个一个函数、变量或者类型时,直接在他名字旁边添加如下属性即可:

__attribute__ ((ATTRIBUTE))
需要注意的是,__attribute__ 后面是两对小括号,不能图方便只写一对,否则会编译报错。括号里的 ATTRIUBTE 表示要声明的属性,目前支持十几种属性声明:

section:自定义段

aligned:对齐

packed:对齐

format:检查函数变参格式

weak:弱声明

alias:函数起别名

noinline:无内联

always_inline:内联函数总是展开

......

当然,对一个变量也可以同时添加多个属性。在定义变量前,各个属性之间用逗号隔开。以下三种声明方式是没有问题的。

 char c  __attribute__((packed, algined(4)));
char c  __attribute__((packed, algined(4))) = 4;
__attribute__((packed, algined(4))) char c = 4;

---------------------------------------------------------------------------------------------------------------------------

__attribute__((unused))   表示可能没有使用,告诉编译器在编译此类型函数或变量时不需报警告(Don't produce a warning)。

unused:This attribute, attached to a function, means that the function is meant to be
        possibly unused. GCC will not produce a warning for this function.

Solution: If variable <variable_name> or function <function_name> is not used, it can be removed. If it is only used sometimes, you can use __attribute__((unused)). This attribute suppresses these warnings. For example:

int  __attribute__ ((unused)) myfunction(int parm1, long parm2) { … }
long __attribute__ ((unused)) myvariable;

If there are a large number of unused variables or functions, which can happen with ported code, you can add the -Wno-unused compiler option to your makefile. This option suppresses all unused warnings.

-O1 -O2 -O3 -Ofast...】C/C++编译器代码优化原理方案
如果不指定优化标识的话,gcc 就会产生可调试代码,每条指令之间将是独立的:可以在指令之间设置断点,使用 gdb 中的 p 命令查看变量的值、改变变量的值等。并且把获取最快的编译速度作为它的目标。

当优化标识被启用之后,gcc 编译器将会试图改变程序的结构(当然会在保证变换之后的程序与源程序语义等价的前提之下),以满足某些目标,如:代码大小最小运行速度更快(只不过通常来说,这两个目标是矛盾的,二者不可兼得)。

在不同的 gcc 配置和目标平台下,同一个标识所采用的优化种类也是不一样的。

可以使用 -Q --help =optimizers 来获取每个优化标识所启用的优化选项。

以下的每个优化标识都可以在 原网址 中找到对应的详细解释。

-O,-O1
在不影响编译速度的前提下,尽量采用一些优化算法,降低可执行代码的大小。

-O2
牺牲部分编译速度,除了执行 -O1 所执行的所有优化之外,还会采用几乎所有的目标配置支持的优化算法,用以 提高目标代码的运行速度。

-O3
除了执行 -O2 所有的优化选项之外,一般都是采取很多向量化算法,提高代码的并行执行程度,利用现代 CPU 中的流水线、Cache等。这个选项会提高执行代码的大小,而可以 提高目标代码的运行速度。

由于 -O3 是以提高执行速度为目的来扩展代码大小,如增加内联等,但过度膨胀反而可能降低执行速度,故而在两者之间取得某种平衡。

-Os

该优化标识和 -O3  的目的恰好相悖。

-Os 会在 -O2 的基础上尽量降低目标代码的大小,压缩代码空间,对嵌入式设备等存储容量小的设备较为重要。

为了降低目标代码大小,会禁用下列优化选项,一般就是压缩内存中的对齐空白1(alignment padding)。

-Ofast          __attribute__((optimize("-Ofast")))
该优化标识将不会严格遵循语言标准,除了启用所有的 -O3 优化选项之外,也会针对某些语言启用部分优化。如:-ffast-math 等。

同时 -Ofast 也会牺牲标准兼容性,以获得更快的运行速度。

-Og
该优化标识会选择部分与 -g 选项不冲突的优化选项,以提供合理的优化水平,同时产生较优秀的可调试信息。
__attribute__((always_inline)) 的意思是强制内联,所有加了 __attribute__((always_inline)) 的函数再被调用时不会被编译成函数调用而是直接扩展到调用函数体内。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值