分享

程序编译链接运行时对库关系的探讨

 喜欢雨路的火焰狼 2006-11-07
对Skymoon兄的文章我学习了一天多,确实够得上深入思考啊
现在才勉强有点顺序感了,我的理解是
say.c源程序调用say.so共享库到test可执行文件,到test的执行,把这个过程分为三步走
第一步:  gcc -c say.c                                     -->    say.o    编译
第二步:  gcc test.c say.o ./say.so -o test     -->    test       链接
第三步:  ./test                                                      把test载入,获得test内存映象
第一步,没话说,gcc是主角
第二步,ld是主角,ld是binutils中安装的工具,他只要搜到了say.so,就能生成test,而gcc test.c ./say.so -o test中的./路径信息,跟ld的LIB_PATH,configure的--with-lib-path(应该就是幻想版主的--lib-path),还有gcc中的-lsay(必须以libsay.so的文件名,存在于ld的默认搜索路径下,就是ld –verbose|grep SEARCH中列出的目录下),-L/aaa/bbb(libsay.so存在/aaa/bbb),这里5个路径信息(欢迎补充),都是告诉ld如何搜索say.so(libsay.so)。至于生成的可执行文件test用哪个ld-linux.so.2由gcc的spec规定,存在于test程序头的PT_INTERP类型的段(segment)中,与ld无关。
第三步,主角是ld-linux.so.2,test要载入内存来运行,需由这个ld-linux.so.2载入必须的共享库,而它搜索库的路径跟上面ld的搜索路径相关的地方就是gcc test.c ./say.so -o test中say.so前面的./会硬编码进test中的动态节(.dynamic section)的DT_NEEDED类型入口中,然而当ld在其他路径信息找到了say.so,却不会把路径信息硬编码进DT_NEED入口。然后就是ld-linux.so.2搜索库的顺序
1    DT_NEED入口中包含的路径
2    DT_RPATH入口给出的路径(存在的话)
3    环境变量LD_LIBRARY_PATH路径(setuid类的程序排除)
4    LD_RUNPATH入口给出的路径(存在的话)
5.   库高速缓存文件ld.so.conf中给出的路径
6.   /lib,/usr/lib
7   当前目录
补充:2  test中的DT_RPATH由什么传递进来,我不清楚。
3    对于setuid类程序,忽略;链接时有—library-path PATH选项时会被override
4    同2,这两处路径,当有链接选项--ignore-rpath LIST时会把LIST中的RPATH和RUNPATH信息忽略掉。
5中ld-linux.so.2找的ld.so.conf则在生成glibc库时的--prefix/etc目录下, ld-linux.so.2其实是找搜索ld.so.cache,由ldconfig生成。
6 当链接时加入了  -z nodeflib 选项,此处路径信息被忽略
第二步中ld找到了say.so,而ld-linux.so.2却找不到时,仅能得到test,却不能生成进程映象。

实验一:
Gcc –fPIC –shared say.c –o say.so
Gcc test.c ./say.so –o test
Readelf –d test | less   (这里的./到了NEEDED里面)
Mkdir 1
Cp say.so 1/libsay.so
Rm test
Gcc –L/root/1 –lsay test.c –o test (能通过,ld找到了libsay.so)
Ldd test   (libsay.so找不到)   (ld-linux.so.2找不到libsay.so)
Readelf –d | less    (这里NEEDED里就是 libsay.so,-L 路径没有包括进来
LD_LIBRARY_PATH=/root/1 ldd test   (这样就找到了)
Rm test
LIB_PATH=/root/1 gcc test.c libsay.so –o test    (不能成功,找不到libsay.so)
Export LIB_PATH=/root/1
Gcc test.c libsay.so –o test     (还是不能成功,因此似乎LIB_PATH只在编译ld时有用?
实验二:
Echo ‘void main(){}’ > hello.c
Gcc –v hello.c   (出现   /usr/lib/gcc/i486-linux-gnu/4.0.4/collect2 --eh-frame-hdr -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 /usr/lib/gcc/i486-linux-gnu/4.0.4/../../../../lib/crt1.o /usr/lib/gcc/i486-linux-gnu/4.0.4/../../../../lib/crti.o /usr/lib/gcc/i486-linux-gnu/4.0.4/crtbegin.o -L/usr/lib/gcc/i486-linux-gnu/4.0.4 -L/usr/lib/gcc/i486-linux-gnu/4.0.4 -L/usr/lib/gcc/i486-linux-gnu/4.0.4/../../../../lib -L/usr/lib/gcc/i486-linux-gnu/4.0.4/../../.. -L/lib/../lib -L/usr/lib/../lib /tmp/ccoNhv8l.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i486-linux-gnu/4.0.4/crtend.o /usr/lib/gcc/i486-linux-gnu/4.0.4/../../../../lib/crtn.o

Perl –pi –e ‘s@ /lib/ld-linux.so.2@ /lib/ld-linux.so.bak@g’ $(gcc –print-file specs)     (把gcc的spec中的linux-ld.so.2改名)
Gcc –v hello.c   (编译仍然能够通过,就是上面信息中ld-linux.so.2 变成ld-linux.so.2.bak)
./a.out      (这是提出ld-linux.so.2.bak不存在,不是合法的程序解释器了)
Rm a.out
Gcc –v –static hello.c    (编译通过, )
./a.out                 (也能运行,哈哈)
至此能证实ld-linux.so.2 在通常的默认的gcc编译时,是动态链接的,上面-v  输出的信息中,其实ld并没有真正调用ld-linux.so.2,只是把gcc的spec中规定的动态连接器名称放入elf文件头中而已,真正调用ld-linux.so.2,要到程序载入阶段)确实如论坛上VirusCamp所言,这里应该就是仅仅写入路径信息到elf文件头中)

现在可以确定了ld-linux.so.2在编译成可执行文件时,是不需要存在的,
现在的问题是ld搜索库的路径和顺序了,在man ld中找到了这么一段
The    linker    uses  the  following  search  paths to locate required
       shared libraries.
1.  Any directories specified by -rpath-link options.

2.  Any directories specified by -rpath  options.   The  difference
between -rpath and -rpath-link is that directories specified by -rpath options are included in the executable and used at  run-time,  whereas the -rpath-link option is only effective at link time. It is for the native linker only.

3.  On an ELF system, if the -rpath and "rpath-link"     options  were
not  used,  search  the    contents  of  the environment variable "LD_RUN_PATH". It is for the native linker only.

4.  On SunOS, if the -rpath option was not used, search any directories specified using -L options.
5.  For  a  native linker, the contents of the environment variable "LD_LIBRARY_PATH".

6.  For a native ELF linker, the  directories  in  "DT_RUNPATH"  or
"DT_RPATH"   of a  shared  library  are searched  for    shared libraries needed by it. The "DT_RPATH" entries are  ignored  if "DT_RUNPATH" entries exist.

7.  The default directories, normally /lib and /usr/lib.

8.  For a    native linker   on  an  ELF    system,     if  the  file /etc/ld.so.conf exists, the list of directories found  in  that file.

If  the required shared library is not found, the linker will issue a warning and continue with the link.
另外还有:
-R filename
 --just-symbols=filename
    Read symbol    names and  their addresses from filename, but do not relocate it or include it in the output.  This allows  your    output file    to refer symbolically to absolute locations of memory defined in other programs.You may use this option more than once.
For compatibility with other ELF linkers, if the -R option is followed  by a directory name, rather than a file name, it is treated as the –rpath option.
关于-LDIR,我以前的理解有很多错误的地方,看:
-Lsearchdir
    --library-path=searchdir
    Add path searchdir to the list of paths that    ld  will  search for
archive  libraries and ld control scripts. You may use this option any number of times.The directories are searched in the order  in which they are specified on the command line.  Directories specified on the command line are searched before the  default  directories. All -L  options apply to all -l options, regardless of the order in which the options appear.

If searchdir begins with "=", then the "=" will be replaced by the sysroot prefix, a path specified when the linker is configured.

The default set of paths searched (without being specified with -L) depends on which emulation mode ld is using, and in some cases also on how it was configured.

The    paths can also  be  specified    in a link  script  with the "SEARCH_DIR" command.  Directories specified this way are  searched
at  the  point  in  which  the linker script appears in the command
line.
这里才明白这个-L DIR 对ld的真正作用
在google搜索了ld LIB_PATH,别人的讨论中,有这么段话,
From the README:

  If you want to make a cross-linker, you may want to specify
  a different search path of -lfoo libraries than the default.
  You can do this by setting the LIB_PATH variable in ./Makefile.

  To build just the linker, make the target all-ld from the top level
  directory (one directory above this one).
那么意思好像是只有当要建立一个交叉编译的ld时,才在建立ld的Makefile中设置变量LIB_PATH,它不是一个环境变量,仅仅是能够设在Makefile或
那么还有configure的 –with-lib-path参数,在构建binutils时仅仅告诉configure脚本在这个路径下找库,那么也就是说跟在生成的Makefile中定义变量LIB_PATH意义一样?

Gcc用的哪个ld?,可以gcc –printf-prog-name=ld  但怎么指定gcc用哪个ld了?
Good2xaut开的帖子,讨论说了,先用gcc本身体系中的的ld,然后在到环境变量PATH中找

到http://gcc.,下面gcc.pdf
-Bprefix  This option specifies where to find the executables, libraries, include files, and
data files of the compiler itself.
Gcc相当于是个司机,驱使其他子程序完成任务,cpp,‘cpp’, ‘cc1’,‘as’ and ‘ld’,在要运行的每个子程序前加上 prefix ,带上或者不带上 /machine/version ,对每个子程序,先试-Bprefix,没找到,或没有B选项,就用标准前缀‘/usr/lib/gcc/’ and ‘/usr/local/lib/gcc/’.
,还没找到,就在环境变量PATH下面找
转化成  -L 选项传给linker,-isystem选项给预处理器preprocessor加上include(搜索头文件的目录)
运行时库libgcc.a,也通过这个选项,还有上面两个标准路径下找
另一种跟B选项相似作用的的方法为,环境变量GCC_EXEC_PREFIX,默认的 ‘prefix/lib/gcc/,prefix为configure脚本传递过来的,-B选项的prefix优先
--sysroot=dir
在前面搜索头文件和库的路径前都加上dir
COMPILER_PATH,跟PATH类似的,在GCC_EXEC_PREFIX下找不到子程序时在这个list下面找
LIBRARY_PATH  对于本地编译器,用来找一些特别的连接器文件,

在gccint.pdf中有:
The program collect2 is installed as ld in the directory where the passes of the compiler
are installed. When collect2 needs to find the real ld, it tries the following file names:
• ‘real-ld’ in the directories listed in the compiler’s search directories.
• ‘real-ld’ in the directories listed in the environment variable PATH.
• The file specified in the REAL_LD_FILE_NAME configuration macro, if specified.
• ‘ld’ in the compiler’s search directories, except that collect2 will not execute itself
recursively.
• ‘ld’ in PATH
“The compiler’s search directories” means all the directories where gcc searches for passes
of the compiler. This includes directories that you specify with ‘-B’.
Cross-compilers search a little differently:
• ‘real-ld’ in the compiler’s search directories.
• ‘target-real-ld’ in PATH.
• The file specified in the REAL_LD_FILE_NAME configuration macro, if specified.
• ‘ld’ in the compiler’s search directories.
• ‘target-ld’ in PATH.




    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多