博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

gcc 选项

Posted on 2019-07-02 10:14  bw_0927  阅读(807)  评论(0)    收藏  举报

链接动态库

如何程序在连接时使用了共享库,就必须在运行的时候能够找到共享库的位置。linux的可执行程序在执行的时候默认是先搜索/lib和/usr/lib这两个目录,然后按照/etc/ld.so.conf里面的配置搜索绝对路径。同时,Linux也提供了环境变量LD_LIBRARY_PATH供用户选择使用,用户可以通过设定它来查找除默认路径之外的其他路径,如查找/work/lib路径,你可以在/etc/rc.d/rc.local或其他系统启动后即可执行到的脚本添加如下语句:LD_LIBRARY_PATH =/work/lib:$(LD_LIBRARY_PATH)。并且LD_LIBRARY_PATH路径优先于系统默认路径之前查找

不过LD_LIBRARY_PATH的设定作用是全局的,过多的使用可能会影响到其他应用程序的运行,所以多用在调试。(LD_LIBRARY_PATH的缺陷和使用准则,可以参考《Why LD_LIBRARY_PATH is bad》 )。通常情况下推荐还是使用gcc的-R或-rpath选项来在编译时就指定库的查找路径,并且该库的路径信息保存在可执行文件中,运行时它会直接到该路径查找库,避免了使用LD_LIBRARY_PATH环境变量查找。

链接选项和路径

现代连接器在处理动态库时将链接时路径(Link-time path)和运行时路径(Run-time path)分开,用户可以通过-L指定连接时库的路径,通过-R(或-rpath)指定程序运行时库的路径,大大提高了库应用的灵活性。
链接器ld的选项有 -L,-rpath 和 -rpath-link,看了下 man ld,大致是这个意思:

-L: “链接”的时候,去找的目录,也就是所有的 -l 选项里的库,都会先从 -L 指定的目录去找,然后是默认的地方。编译时的-L选项并不影响环境变量LD_LIBRARY_PATH,-L只是指定了程序编译连接时库的路径,并不影响程序执行时库的路径,系统还是会到默认路径下查找该程序所需要的库,如果找不到,还是会报错,类似cannot open shared object file。

-rpath-link:这个也是用于“链接”的时候的,例如你显示指定的需要 FOO.so,但是 FOO.so 本身是需要 BAR.so 的,后者你并没有指定,而是 FOO.so 引用到它,这个时候,会先从 -rpath-link 给的路径里找。

-rpath: “运行”的时候,去找的目录。运行的时候,要找 .so 文件,会从这个选项里指定的地方去找。对于交叉编译,交叉编译链接器需已经配置 –with-sysroot 选项才能起作用。也就是说,-rpath指定的路径会被记录在生成的可执行程序中,用于运行时查找需要加载的动态库。

 

-rpath=dir

Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects. All -rpath arguments are concatenated and passed to the runtime linker, which uses them to locate shared objects at runtime. The -rpath option is also used when locating shared objects which are needed by shared objects explicitly included in the link; see the description of the -rpath-link option. If -rpath is not used when linking an ELF executable, the contents of the environment variable LD_RUN_PATH will be used if it is defined.


-rpath-link=dir

When using ELF or SunOS, one shared library may require another. This happens when an ld -shared link includes a shared library as one of the input files. When the linker encounters such a dependency when doing a non-shared, non-relocatable link, it will automatically try to locate the required shared library and include it in the link, if it is not included explicitly. In such a case, the -rpath-link option specifies the first set of directories to search. The -rpath-link option may specify a sequence of directory names either by specifying a list of names separated by colons, or by appearing multiple times. 
This option should be used with caution as it overrides the search path that may have been hard compiled into a shared library. In such a case it is possible to use unintentionally a different search path than the runtime linker would do.


The difference between -rpath and -rpath-link is that directories specified by -rpath options are included in the executable and used at runtime, whereas the -rpath-link option is only effective at link time. Searching -rpath in this way is only supported by native linkers and cross linkers which have been configured with the --with-sysroot option.

 

https://stackoverflow.com/questions/6562403/i-dont-understand-wl-rpath-wl

man gcc:

-Wl,option

Pass option as an option to the linker. If option contains commas, it is split into multiple options at the commas. You can use this syntax to pass an argument to the option. For example, -Wl,-Map,output.map passes -Map output.map to the linker. When using the GNU linker, you can also get the same effect with `-Wl,-Map=output.map'.

man ld:

-rpath=dir

Add a directory to the runtime library search path. This is used when linking an ELF executable with shared objects. All -rpath arguments are concatenated and passed to the runtime linker, which uses them to locate shared objects at runtime. The -rpath option is also used when locating shared objects which are needed by shared objects explicitly included in the link;

 

243
 

The -Wl,xxx option for gcc passes a comma-separated list of tokens as a space-separated list of arguments to the linker. So

gcc -Wl,aaa,bbb,ccc

eventually becomes a linker call

ld aaa bbb ccc

In your case, you want to say "ld -rpath .", so you pass this to gcc as -Wl,-rpath,. Alternatively, you can specify repeat instances of -Wl:

gcc -Wl,aaa -Wl,bbb -Wl,ccc

Note that there is no comma between aaa and the second -Wl.

Or, in your case, -Wl,-rpath -Wl,..

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

-Wl,-rpath .  和 -Wl,-rpath=.  效果一样
-Wl,-rpath,/path/to/foo -L/path/to/foo -lbaz

or you may end up with an error like

ld: cannot find -lbaz

 

 ===========

gcc/g++中生成map文件

在通过gcc/g++间接调用链接程序ld时,所有的ld选项前必须加上“-Wl,”,因为-Map是ld的选项所以,要让g++生成mapfile,需要增加编译参数“ -Wl,-Map,mapfile”。

例:gcc -o helloworld helloworld.c -Wl,-Map,helloworld.map

 

http://www.360doc.com/content/14/0626/14/7377734_389888251.shtml

一般的大型工程都会在生成可执行文件的同时让链接器生成一个map文件,从而大致查看一下可执行文件中符号的内存布局以及从哪里引入可执行文件。这个通常对于小型工程是作用不大,因为代码就那么多,随便grep一下就知道符号定义位置了。但是对于一些大型工程或者涉及了比较多的第三方库、或者涉及了比较多的功能模块的时候,就需要知道这些符号是在哪里定义,或者说如果一个符号引用了但是没有知道函数定义,此时也需要找到这个符号是哪个模块引入的,为什么需要,所以需要一些通用的(形式化)的方法来搜索这些符号,而map文件就是一个比较好的切入点。但是map符号并不是万能的,它只能列出参与链接的全局变量的位置以及在哪个模块,对于一些静态变量,map文件中并不能体现它们,而在没有特殊声明的情况下,可执行文件中将会包含静态符号在符号表中,所以有时候我们只能依赖可执行文件本身里面的符号表来猜测一个符号的定义位置。说起静态符号,还有就是它是如何保证它只在一个编译模块中可见和被引用,而对其它模块不可见?


--------------------- 
作者:李子 
来源:CSDN 
原文:https://blog.csdn.net/liyongming1982/article/details/6663714 
版权声明:本文为博主原创文章,转载请附上博文链接!

 


http://my.huhoo.net/archives/2010/03/post-52.html

在普通的C语言中,如果您希望将函数或者变量限制在当前文件中,需要对其使用static关键字。

然而,在一个包含很多文件的共享库中,如果您希望某个符号可以被共享库内部的几个文件访问,而又不提供给外部,则对符号进行隐藏处理就会比较困难。大多数的连接器都提供一些便利的方法来隐藏或者显示模块中所有的符号,但如果希望更加具有选择性,则需要更多的处理。

    在Mac OS X v10.4之前,有两种机制可以控制符号的可见性。
    第一种技术是通过__private_extern__关键字,将符号声明为共享库私有,而又可以从当前文件输出。这个关键字可以使用到static或extern关键字可以使用的任何地方;
    第二种技术是使用输出列表。输出列表是一个文件,含有您希望隐藏或者显示的符号名称。C语言的符号名称比较容易确定(在名称前面加一个下划线字符就可以),C++的符号名称的确定则要复杂得多。由于有类和名字空间,编译器必须包含更多的信息才能唯一标识每一个符号,编译器也因此为每个符号创建一个所谓的重整名称(mangled name)。这种重整名称的生成规则通常和编译器有关,难于进行逻辑的推断,也很难在共享库定义的大列表中找到。

    幸运的是,GCC 4.0提供了一些新的、用于改变符号可见性的方法。下面部分将对这些新方法进行描述:

一、使用GCC 4.0来标识符号的可见性
    从Mac OS X v10.4开始,要隐藏C++符号名称就简单很多了。GCC 4.0编译器支持一些用于隐藏和显示符号名称的新选项,同时还支持一个新的pragma和一些编译器属性,用于改变代码符号的可见性。

    请注意:下面的特性只在GCC 4.0和更新的版本提供。

  • 编译器选项
    GCC 4.0支持一个新的选项,用于设置源文件中符号的缺省可见性。这个选项是-fvisibility=vis ,您可以用它来设置当前编译的符号的可见性。这个选项的值可以是default(缺省)或者hidden(隐藏),设置为default时,没有显式标识为hidden的符号都处理为可见;设置为hidden时,没有显式标识为可见的符号都处理为隐藏。如果您在编译中没有指定-fvisibility选项,编译器会自行处理为缺省的可见性。

    请注意:default设置不是指编译器缺省的处理方式。和hidden设置一样,default来自ELF格式定义的可见性名称。具有default可见性的符号和所有不使用特殊机制的符号具有相同的可见性类型&8212;也就是说,它将作为公共接口的一部分输出。

    编译器还支持-fvisibility-inlines-hidden选项,用于强制隐藏所有的嵌入函数。当您希望对大多数项目使用缺省的可见性,但又希望隐藏所有的嵌入函数时,您可能会用到这个选项。

  • 可见性属性

如果用GCC 4.0编译代码,可以用可见性属性将个别的符号标识为default或hidden:

__attribute__((visibility("default"))) void MyFunction1() {}   
__attribute__((visibility("hidden"))) void MyFunction2() {}

    可见性属性会覆盖编译时通过-fvisibility选项指定的值。因此,增加default可见性属性会使符号在所有情况下都被输出,反过来,增加hidden可见性属性会隐藏相应的符号。

    可见性属性可以应用到函数,变量,模板,以及C++类。如果一个类被标识为hidden,则该类的所有成员函数,静态成员变量,以及编译器生成的元数据,比如虚函数表和RTTI信息也都会被隐藏。

    请注意:虽然模板声明可以用可见性属性来标识,但是模板实例则不能。这是个已知的限制,在GCC的未来版本中可能被修复。

    为了演示这些属性如何在编译时进行工作,请看一下下面的声明:

int a(int n) {return n;}
__attribute__((visibility("hidden"))) int b(int n) {return n;}
__attribute__((visibility("default"))) int c(int n) {return n;}
class X
{
    public:
        virtual ~X();
};

class __attribute__((visibility("hidden"))) Y
{
    public:
        virtual ~Y();
};

class __attribute__((visibility("default"))) Z
{
    public:
        virtual ~Z();
};

X::~X() { }
Y::~Y() { }
Z::~Z() { }
    用-fvisibility=default选项编译这段代码会使函数a和c ,还有类X和Z的符号输出到库的外部。而如果用-fvisibility=hidden选项进行编译,则会输出函数c和类Z的符号。

    从实践上看,用可见性属性标识符号的可见或者隐藏比起用__private_extern__关键字隐藏个别的符号要好。__private_extern__关键字采用的方法是缺省暴露所有的符号,然后选择性地隐藏所有的符号。在大型的共享库中,相反的方法通常更好一些。因此,隐藏所有的符号,然后有选择地暴露希望客户使用的符号通常会好一些。

    为了简化将符号标识为输出的任务,您可能希望将default的可见性属性集合定义为宏,如下面的例子所示:
#define EXPORT __attribute__((visibility("default")))
// Always export the following function.
EXPORT int MyFunction1();
    使用宏的优点是当您的代码也需要在其它平台编译的时候,可以将宏改为适用于其它平台的关键字。
  • Pragmas
    将符号标识为default或者hidden的另外一种方法是使用GCC 4.0新引入的pragma指令。GCC可见性pragma的优点是可以快速地标识一整块函数,而不需要将可见性属性应用到每个函数中。这个pragma的用法如下:
void f() { }
#pragma GCC visibility push(default)
void g() { }
void h() { }
#pragma GCC visibility pop
    在这个例子中,函数g和h被标识为default,因此无论-fvisibility选项如何设置,都会输出;而函数f则遵循-fvisibility选项设置的任何值。push和pop两个关键字标识这个pragma可以被嵌套。
二、限制符号可见性的原因
    从动态共享库中尽可能少地输出符号是一个好的实践经验。输出一个受限制的符号会提高程序的模块性,并隐藏实现的细节。在库中减少符号的数目还可以减少库的内存印迹,减少动态连接器的工作量。动态连接器装载和识别的符号越少,程序启动和运行的速度就越快。
三、嵌入函数的可见性
     嵌入函数通常在调用现场被展开,因此在目标文件中完全不表示为符号。然而在很多情况下,由于一些很好的理由,编译器可能会表示出嵌入函数的函数体,并因此为其生成符号。最为常见的情况是如果所有的优化都被禁止了,编译器可能决定不进行嵌入优化;较为少见的情况是函数太大,不能进行嵌入,或者函数的地址在其它地方被使用,因此需要有一个符号。

     虽然在C++中,您可以将可见性属性(参见“可见性属性”部分)应用到嵌入函数中,就如同其它符号,但是隐藏所有的嵌入函数通常要更好一些。从动态共享库中输出嵌入函数会带来一些复杂的问题。因为有几个因素跟编译器的选择(是将函数表示出来,还是进行嵌入处理)有关系,和共享库的不同连编版本一起连编客户程序的时候,可能会碰到错误。

     C和 C++的嵌入函数语法有一些细微的区别,记住这一点也是很重要的。在C程序中,只有一个源代码文件可以为一个嵌入函数提供out-of-line(译者注:与inline相对应,指不进行嵌入展开)的定义。这意味着C程序员对out-of-line的拷贝驻留的位置有精确的控制。因此对于基于C的动态共享库来说,只输出嵌入函数的一个拷贝是可能的。对于C++,嵌入函数的定义必须包含在每个使用该函数的翻译单元中。因此如果编译器表示了一个out- of-line的拷贝,则可能会有几个拷贝驻留在不同的翻译单元中。

      最后,如果希望隐藏所有的嵌入函数(但不希望隐藏所有的其它代码),可以用在代码编译的时候使用-fvisibility-inlines-hidden选项。如果您已经向编译器传递了-fvisibility=hidden选项,则没有必要使用-fvisibility-inlines-hidden选项。
四、符号可见性和Objective-C
     Objective- C是C的限制超集, Objective-C++则是C++的限制超集。这意味着这里关于C和C++符号可见性的所有讨论都同样可以应用到Objective-C和 Objective-C++上。您可以用编译器选项,可见性属性,以及可见性pragma来隐藏Objective-C代码文件中的C和C++代码。然而,这些可见性控制只能应用到代码中的C或者C++子集,不能应用到Objective-C的类和方法上。

     Objective-C类和消息名称由Objective-C运行环境来限制,而不是通过连接器,因此可见性的说明对它们是不起作用的。我们无法在共享库的客户程序中隐藏共享库定义的Objective-C类。

 


 

 

https://blog.csdn.net/tl_sunshine/article/details/78936572

https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html#Debugging-Options

利用gdb单步执行理解刚接手的程序的时候,提示下图中的错误:

 

 

查了很多的资料,找了很多的解决办法:

1、更改gdb7.2版本到gdb7.8; 

2、编译的时候加入-gdwarf-2等编译选项。

都不能解决问题。

 

后来认真看了编译程序的BUILD文件,发现程序加入了-fvisibility=hidden以及-Wl,-Bsymbolic这些链接选项。

恍然大悟。加入-fvisibility=hidden这个选项导致不能看到行号等信息。

 

那当初编译程序的人为什么加入“-fvisibility=hidden”或者“-Wl,-Bsymbolic”这些链接选项呢?

主要是想解决多个共享库符号表冲突的问题。每个共享库用自己库中的符号表。

具体含义也可以参见:

https://stackoverflow.com/questions/6538501/linking-two-shared-libraries-with-some-of-the-same-symbols/6540059#6540059

另外,对于编写c++程序,要养成好的编程习惯,加入命名空间,减少符号冲突的可能。
————————————————
版权声明:本文为CSDN博主「tl_sunshine」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tl_sunshine/article/details/78936572

 

 


 

https://blog.csdn.net/pengfei240/article/details/55228228

背景
有时我们的程序会定义一些暂时使用不上的功能和函数,虽然我们不使用这些功能和函数,但它们往往会浪费我们的ROM和RAM的空间。这在使用静态库时,体现的更为严重。有时,我们只使用了静态库仅有的几个功能,但是系统默认会自动把整个静态库全部链接到可执行程序中,造成可执行程序的大小大大增加。

参数详解
为了解决前面分析的问题,我们引入了标题中的几个参数。GCC链接操作是以section作为最小的处理单元,只要一个section中的某个符号被引用,该section就会被加入到可执行程序中去。因此,GCC在编译时可以使用 -ffunction-sections 和 -fdata-sections 将每个函数或符号创建为一个sections,其中每个sections名与function或data名保持一致。而在链接阶段, -Wl,–gc-sections 指示链接器去掉不用的section(其中-wl, 表示后面的参数 -gc-sections 传递给链接器),这样就能减少最终的可执行程序的大小了。

我们常常使用下面的配置启用这个功能:

CFLAGS += -ffunction-sections -fdata-sections
LDFLAGS += -Wl,--gc-sections
1
2
例子
main.c 文件如下:

#include <stdio.h>

int fun_0(void)
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}

int fun_1(void)
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}

int fun_2(void)
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}

int fun_3(void)
{
printf("%s: %d\n", __FUNCTION__, __LINE__);
return 0;
}

void main(void)
{
fun_0();
fun_3();
}

Makefile文件如下:

main_sections:
gcc -ffunction-sections -fdata-sections -c main.c
gcc -Wl,--gc-sections -o $@ main.o

main_normal:
gcc -c main.c
gcc -o $@ main.o

clean:
rm -rf *.o main_sections main_normal

验证
运行
$ make main_sections
gcc -ffunction-sections -fdata-sections -c main.c
gcc -Wl,--gc-sections -o main_sections main.o
$ make main_normal
gcc -c main.c
gcc -o main_normal main.o

比较大小
$ ll main_*
-rwxrwxr-x 1 8896 2月 16 00:42 main_normal*
-rwxrwxr-x 1 8504 2月 16 00:42 main_sections*

可以看见使用该功能的二进制文件要小于不使用该功能的二进制文件

分析sections
$ make clean
rm -rf *.o main_sections main_normal
$ make main_sections
gcc -ffunction-sections -fdata-sections -c main.c
gcc -Wl,--gc-sections -o main_sections main.o
$ readelf -t main.o
...
[ 5] .text.fun_0
PROGBITS PROGBITS 0000000000000000 0000000000000048 0
0000000000000024 0000000000000000 0 1
[0000000000000006]: ALLOC, EXEC
[ 6] .rela.text.fun_0
RELA RELA 0000000000000000 0000000000000508 24
0000000000000048 0000000000000018 5 8
[0000000000000040]: INFO LINK
[ 7] .text.fun_1
PROGBITS PROGBITS 0000000000000000 000000000000006c 0
0000000000000024 0000000000000000 0 1
[0000000000000006]: ALLOC, EXEC
[ 8] .rela.text.fun_1
RELA RELA 0000000000000000 0000000000000550 24
0000000000000048 0000000000000018 7 8
[0000000000000040]: INFO LINK
[ 9] .text.fun_2
PROGBITS PROGBITS 0000000000000000 0000000000000090 0
0000000000000024 0000000000000000 0 1
[0000000000000006]: ALLOC, EXEC
[10] .rela.text.fun_2
RELA RELA 0000000000000000 0000000000000598 24
0000000000000048 0000000000000018 9 8
[0000000000000040]: INFO LINK
[11] .text.fun_3
PROGBITS PROGBITS 0000000000000000 00000000000000b4 0
0000000000000024 0000000000000000 0 1
[0000000000000006]: ALLOC, EXEC
[12] .rela.text.fun_3
RELA RELA 0000000000000000 00000000000005e0 24
0000000000000048 0000000000000018 11 8
[0000000000000040]: INFO LINK


从object文件中可以发现,fun_0 ~ fun_3 每个函数都是一个独立的section.
而如果使用 make main_normal 生成的object文件,则共享一个默认的sections(.text)。

分析elf文件
$ readelf -a main_normal | grep fun_
52: 0000000000400526 36 FUNC GLOBAL DEFAULT 14 fun_0
55: 000000000040056e 36 FUNC GLOBAL DEFAULT 14 fun_2
65: 0000000000400592 36 FUNC GLOBAL DEFAULT 14 fun_3
66: 000000000040054a 36 FUNC GLOBAL DEFAULT 14 fun_1
$ readelf -a main_sections | grep fun_
49: 0000000000400526 36 FUNC GLOBAL DEFAULT 14 fun_0
57: 000000000040054a 36 FUNC GLOBAL DEFAULT 14 fun_3

可以看见,在最终的目标文件中,未使用的函数并未被链接进最终的目标文件。
————————————————
版权声明:本文为CSDN博主「p_fly」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/pengfei240/article/details/55228228