最近解决一个动态链接上的问题,因为以前从来没有接触过这方面的知识,所以恶补了一下,首先要了解gcc编译指令(makefile),ld链接器的选项(还有连接脚本section指定内存位置),熟悉查看连接状态是否成功的指令工具(其中又有elf格式,ld、elf相关指令)。然后反汇编理解动态链接的实际执行过程,总的说来这里面的东西还真很多,主要是网上比较深入资料很少,大部分博文也是一知半解,所以后续有问题应直接阅读标准文档。《程序员的自我修养----链接、装载与库》这本书详细介绍了一个应用程序在编译、链接和运行时刻所发生的各种事项,包括:代码指令是如何保存的,库文件如何与应用程序代码静态链接,应用程序如何被装载到内存中并开始运行,动态链接如何实现。

Makefile:

LOCAL_SRC_FILES := hello.c PREFIX := arm-anykav200-linux-uclibcgnueabi-

LOCAL_SHARED_LIBRARIES := pthread

LOCAL_MODULE := hello

CC  := $(PREFIX)gcc CPP  := $(PREFIX)g++ AS  := $(CC) LD  := $(PREFIX)ld

BUILD_DIR=./

SONAME  := $(LOCAL_MODULE).elf

CROSS_PATH      ?= /opt/arm-anykav200-crosstool/usr ARM_LIBC_PATH ?=$(CROSS_PATH)/arm-anykav200-linux-uclibcgnueabi/sysroot/usr/lib ARM_LIBGCC_PATH ?=$(CROSS_PATH)/lib/gcc/arm-anykav200-linux-uclibcgnueabi/4.8.5 CLIB =  $(ARM_LIBC_PATH)/libm.a CLIB += $(ARM_LIBC_PATH)/libc.a CLIB += $(ARM_LIBGCC_PATH)/libgcc.a CLIB += $(ARM_LIBGCC_PATH)/libgcc_eh.a

CFLAGS  := -Wall -fPIC -fno-builtin -nostdlib -mlong-calls LDFLAGS  := -p -X  -y memset -y memcpy -y mi  -Map maps.txt -T link.lds --wrap=malloc -l:../so/libmi.so -l:../ko/libko.so

C_SRC  := $(filter %.c,$(LOCAL_SRC_FILES)) C_OBJ  := $(patsubst %.c,$(BUILD_DIR)/%.o,$(C_SRC))

.PHONY: all clean

LIBC_OS := $(wildcard libc/*.os)

TARGET  := $(BUILD_DIR)/$(SONAME)

all: $(TARGET)

#主要是这里的链接顺序问题,优先链接libc的静态库,防止链接到了动态库里的libc段

$(TARGET): $(C_OBJ)  $(LD) -o $@ $^ -dn $(CLIB) -dy $(LDFLAGS)

$(C_OBJ): $(BUILD_DIR)/%.o: %.c  $(CC) $(CFLAGS) -c $< -o $@

clean:  rm -rf *.o *.so *.elf *.txt

// 学习笔记

Gcc 链接器ld选项参数解释: -shared:  该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号)。不用该标志外部程序无法连接。

相当于一个可运行文件

-fpic:  表示编译为位置独立的代码。不用此选项的话编译后的代码是位置相关的所以动态加载时是通过代码拷贝的方式来满足不同进程的须要,而不能达到真正代码段共享的目的。

-l 选项告诉编译器要使用hello这个库。奇怪的地方是动态库的名字是libhello.so,这里却使用hello. 但这样还不行。编译会出错。

In function `main': test.c:(.text+0x1d): undefined reference to `hello' collect2: ld returned 1 exit status 这是由于hello这个库在我们自己的路径中,编译器找不到。

须要使用-L选项,告诉hello库的位置 gcc test.c -lhello -L. -o test -L .告诉编译器在当前文件夹中查找库文件

-Wl选项告诉编译器将后面的参数传递给链接器。(给编译器用的)

-soname则指定了动态库的soname(简单共享名,Short for shared object name)

当要升级系统中的一个库时,并且新库的soname和老库的soname一样,用旧库链接生成的程序使用新库依然能正常运行。这个特性使得在Linux下,升级使得共享库的程序和定位错误变得十分容易。

-Wl,rpath=<your_lib_dir>选项

gcc编译链接动态库时,很有可能编译通过,但是执行时,找不到动态链接库,那是

因为-L选项指定的路径只在编译时有效,编译出来的可执行文件不知道-L选项后面的值,

当然找不到。可以用ldd <your_execute>看看是不有 ‘not found’在你链接的库后面,

解决方法是通过-Wl,rpath=<your_lib_dir>,使得execute记住链接库的位置

-nostdlib:不链接C语言的标准库 仅搜索那些在命令行上显式指定的库路径. 在连接脚本中(包含在命令行上指定的连接脚本)指定的库路径都被忽略.

-fno-builtin -fno-builtin-function  不接受没有 __builtin_ 前缀的函数作为内建函数。

-y SYMBOL'`--trace-symbol=SYMBOL'打印出所有SYMBOL出现的被连接文件的名字. 这个选项可以被多次使用. 在很多系统中,这在预先确定底线时很有必要.当你拥有一个未定义的符号,但不知道这个引用出自哪里的时候,这个选项有用.

–as-needed

只连接使用到的动态库,连接顺序从左往右

Makefile有三个非常有用的变量。分别是$@,$^,$<代表的意义分别是: $@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件。

工具指令:

1、查看调用关系 arm-anykav200-linux-uclibcgnueabi-ldd hello.elf

2、readelf -r hello.elf  显示动态连接,初始的symbol是0

Relocation section '.rel.dyn' at offset 0x893c contains 3 entries:

Offset     Info    Type            Sym.Value  Sym. Name

800009e0  00000d15 R_ARM_GLOB_DAT    00000000   __wrap_malloc

800009e4  00001015 R_ARM_GLOB_DAT    00000000   mi

800009e8  00001315 R_ARM_GLOB_DAT    00000000   memset

3、readelf -s -D  hello.elf

Ndx为1表示在.text属于静态连接;Ndx是UND,表明这个符号没有在SimpleSection.o中定义,仅仅是被引用

Symbol table for image:

Num Buc:    Value  Size   Type   Bind Vis      Ndx Name

24   0: 800005b0     0 NOTYPE  GLOBAL DEFAULT   1 __fsymtab_start

17   0: 800005b0     0 NOTYPE  GLOBAL DEFAULT   1 __text_end

10   2: 800005b0     0 NOTYPE  GLOBAL DEFAULT   1 __rtmsymtab_end

20   3: 800005b0     0 NOTYPE  GLOBAL DEFAULT   1 __vsymtab_start

6   3: 800005b0     0 NOTYPE  GLOBAL DEFAULT   1 __fsymtab_end

5   3: 800005b0     0 NOTYPE  GLOBAL DEFAULT   1 __rt_init_start

21   4: 800005b0     0 NOTYPE  GLOBAL DEFAULT   1 __vsymtab_end

15   4: 800009f8     0 NOTYPE  GLOBAL DEFAULT   9 __data_end

8   4: 800005b0     0 NOTYPE  GLOBAL DEFAULT   1 __rt_init_end

14   5: 800005b0     0 NOTYPE  GLOBAL DEFAULT   1 __rodata_start

12   6: 800009f8     0 NOTYPE  GLOBAL DEFAULT   9 __bss_end

13   8: 00000000     0 FUNC    GLOBAL DEFAULT UND __wrap_malloc

4、arm-anykav200-linux-uclibcgnueabi-objdump -h -D libmi.so > mi.txt

将中间文件dump到反汇编文件中

Elf相关概念:(动态连接及执行过程)

首先理解下plt是procedure linkage table,got是global offset table。got表中存放的是外部符号的地址。plt表中存放的是函数地址

如果一个动态库函数是第一次被调用,那么plt表中是不存在该函数的地址的,通过ld库中的函数,将这个地址取出来存放到got表中,那么当第二次调用该函数时,plt表中就有了这个函数的地址,直接跳转到该地址,而不再需要去取地址,也就是动态链接。

GOT 是 data section, 是一个 table, 除专用的几个 entry,每个 entry 的内容可以再执行的时候修改;

PLT 是 text section, 是一段一段的 code,执行中不需要修改。

elf文件中的.plt .rel.dyn .rel.plt .got .got.plt的关系

.plt的作用是一个跳板,保存了某个符号在重定位表中的偏移量(用来第一次查找某个符号)和对应的.got.plt的对应的地址(延时绑定)

.rel.dyn重定向表,在程序启动时就需要重定位完成。

.rel.plt保存了重定位表的信息,可以使用lazy(延时绑定)的连接方式

.got据说是保存了elf文件本身的各个符号的偏移量,即不要动态链接,未证明

.got.plt保存了重定位地址。

比如printf是一个重定位符号,需要连接该符号时过程是这样:

main函数call  .plt段中的一个地址,这里的第一句话就是调转到.got.plt中的保存的printf的地址,如果是第一次,那么保存的地址就是.plt中的下一句话,这个下一句话就是压入这个符号在.rel.plt中的重定位表的偏移量,然后ld程序就会根据重定位表中的信息加上这个偏移量找到这个地址,保存到重定位表所指向的地址中,这个地址其实就是.got.plt段的一个地址。

第二次调用时就可以直接获取到.got.plt中保存的地址了。

附录,参考文献:

1、https://blog.csdn.net/dean_yanqing/article/details/6669352  ld链接器选项说明

2、http://www.cnblogs.com/OCaml/archive/2012/06/18/2554086.html#sec-1-6-1 链接顺序说明

3、https://www.cnblogs.com/gatsby123/p/9750187.html   https://greek0.net/elf.html elf格式说明

4、

gcc ld 链接器相关知识,调试指令(程序员的自我修养----链接、装载与库)的更多相关文章

  1. bss、弱符号强符号、common块、未初始化的全局变量------程序员的自我修养-链接装载与库

  2. 程序员的自我修养九Windows下的动态链接

    9.1 DLL简介 DLL即动态链接库的缩写,它相对于Linux下的共享对象. Windows下的DLL文件和EXE文件实际上是一个概念,它们都是有PE格式的二进制文件. 微软希望通过DLL机制加强软 ...

  3. vs2008调试 Release(链接器来生成调试信息)

    VS2008 Release 修改配置: 1.项目——>属性——>C/ C++ ——> 常规 ——>调试信息格式——>用于“编辑并继续”的程序数据库(/ZI) 2.项目— ...

  4. 你好,C++(4)2.1.3 我的父亲母亲:编译器和链接器 2.1.4 C++程序执行背后的故事

    2.1.3  我的父亲母亲:编译器和链接器 从表面上看,我是由Visual Studio创建的,而实际上,真正负责编译源代码创建生成可执行程序HelloWorld.exe的却是Visual Studi ...

  5. linux实用指令 | 程序员线上排查必知必会linux指令(持续更新中)

    Linux线上排查程序员实用指南 一.乱码问题 二.帮助指令 1. help命令 2. man命令 3. info命令 三.性能监测与优化 1. top命令 参考资源 Linux线上排查程序员实用指南 ...

  6. npm包管理器相关知识

    关于npm包安装命令的介绍,如下图:

  7. linux下c程序的链接、装载和库(1)

    读完<程序员的自我修养--链接.装载和库>相关章节,想来总结一下,若有错误,请指正,多谢. 1. 什么叫目标文件? 你的工程里有很多xxx.c这样的源文件,这些文件是文本文件,只有人能够认 ...

  8. 笔记:LNK2001不代表链接器真的需要链接相关符号

    环境:VS2008   我们都知道,链接器在生成可执行程序时,会忽略那些没有用到的符号.但是昨天遇到一个链接问题,看起来与这条基本策略并不相符.首先看一个静态链接库的结构:   lib | |---- ...

  9. muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor

    目录 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor Connector 系统函数connect 处理非阻塞connect的步骤: Connetor时序图 Accep ...

随机推荐

  1. Ionic 2 + 手动搭建开发环境教程 【转】

    ionic简介 为什么选用ionic: 彻底开源且免费 性能优异 基于红的发紫的AngularJs 漂亮的UI 强大的命令行(基于更热门的nodejs) 开发团队非常活跃. ngCordova,将主流 ...

  2. JavaScript中call和apply方法的使用

    acvaScript中的call()方法和apply()方法,在某些时候这两个方法还确实是十分重要的.1. 每个函数都包含两个非继承而来的方法:call()方法和apply()方法.2. 相同点:这两 ...

  3. oo第3次博客作业

    一.规格化设计的发展历史 20世纪60年代,软件出现严重的危机Dijkstra提出了goto语句的危害,由此引发了软件界长达数年的论战,并产生了结构化的程序设计方法.随着计算机 技术的发展,结构设计化 ...

  4. nova98 假区域链 骗人项目(vexx.pro的前身)

    首先,我是受害者. nova98前期是vexx.pro,前期推广送比特龙, 送3个,然后推广一个新人可以再拿到1.5个. 然后呢,现在就又推出一个新网站,nova98,把之前推广的人领到币全部清零,而 ...

  5. 十分钟搞定 pandas

    原文:http://pandas.pydata.org/pandas-docs/stable/10min.html 译者:ChaoSimple 校对:飞龙 官方网站上<10 Minutes to ...

  6. 1个多商户、多平台版 微信小程序(多商户、多平台版),影城行业、影业连锁 多商户、多平台版微信小程序。(基于多平台版,支持在业务上 可给 每个单独影城 分发定制单独的小程序版本)

    1个 影城行业 微信小程序(多商户.多平台版), 影业连锁 多商户.多平台版微信小程序.(基于多平台版,支持在业务上 可给 每个单独影城 分发定制单独的小程序版本) 资讯QQ: 876635409  ...

  7. mybatis 源码分析二

    1.SqlSession下的四大对象 Executor.StatementHandler.ParameterHandler.ResultSetHandler StatementHandler的作用是使 ...

  8. 安装ruby及sass

    下载 ruby安装包,安装后,进入: 输入: gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/ ...

  9. 软件安装配置笔记(三)——ArcGIS系列产品安装与配置(补档)(附数据库连接及数据导入)

    在前两篇安装配置笔记之后,就忘记把其他安装配置笔记迁移过来了,真是失误失误!趁现在其他文档需要赶紧补上. 目录: 一.ArcMap 二.ArcMap连接数据库并导入数据 三.Arcgis Pro 四. ...

  10. 加载xib文件,如果想在初始化的时候就添加点东西就重载-(id)initWithCoder:(NSCoder *)aDecoder

    - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { self.cl ...