61
五竹 一淘搜索与算法 Linux C++ 编程之链接与装载 础篇

Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

  • Upload
    tidesq

  • View
    8.101

  • Download
    18

Embed Size (px)

DESCRIPTION

主要介绍编译,链接与装载的基本概念与原理静态链接常见错误分析,静态链接常用链接选项使用讲解dlopen/dlsym?常用链接选项讲解

Citation preview

Page 1: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

五竹

一淘搜索与算法

Linux C++ 编程之链接与装载

—基础篇

Page 2: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

课程大纲页

• 编译与链接的基本概念

• 常见链接错误分析

• 静态链接

• 动态库 与 动态链接基础

• 常用链接选项

• 工具介绍

Page 3: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

编译与链接的基本概念

Page 4: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

编译与链接的基本概念

//hello.c

#include <stdio.h>

int main()

{

printf("Hello World\n");

return 0;

}

编译:

gcc hello.c

Page 5: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

编译与链接的基本概念

Page 6: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

编译与链接的基本概念—预处理

• 预处理(Prepressing) : -E 表示只进行预处理, 即 : gcc –E hello.c –o

hello.i

展开所有 #define 定义的宏

处理所有条件预编译指令,如 #if, #ifdef

递归的将 #include 的文件插入到该预编译文件中

删除各类注释

添加行和文件标识,如 #2 "hello.c" 2 ,用于调试或编译出错报警

保留所有的 #pragma 编译指令,编译器要使用

• 对于C++ 来说,预处理后的文件扩展名是 .ii

Page 7: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

编译与链接的基本概念—编译

• 编译(Compilation): gcc –S hello.i –o hello.s

编译过程就是把预处理完的文件进行一系列词法分析,

语法分析,语义分析及优化后生成相应的汇编代码文件.

• 现在的gcc 把预处理和编译两个步骤合成一个步骤,C语言

使用一个叫做 cc1 的程序来完成,C++则是 cc1plus ,位于

/usr/libexec/gcc/x86_64-redhat-linux/4.1.2/

• gcc 实际上是这些后台程序的包装,它会根据参数要求去调

用 cc1(cclplus), 汇编器 as 和链接器 ld

Page 8: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

编译与链接的基本概念—汇编

• 汇编(Assembly) :

gcc –C hello.s –o hello.o

或 as hello.s -o hello.o

• 汇编器是将编译语言转换成机器可以执行的指令.

Page 9: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

编译与链接的基本概念—链接

• 链接(Linking):解决一个程序被分割成多个模块后,

模块间最后如何组合成一个单一程序的问题.

• 链接的主要任务是把各个模块之间相互引用的部

分处理好,使各个模块之间能正确的衔接.

Page 10: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

常见链接错误分析 calu.c

#include <math.h> #include <stdio.h> int main (void) {

double x = sqrt (2.0); printf ("The square root of 2.0 is %f\n", x); return 0;

} gcc -Wall calu.c -o calc -fno-builtin

/tmp/ccKPlcED.o: In function `main':calu.c:(.text+0x1c): undefined reference to `sqrt'collect2: ld 返回 1

Page 11: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

常见链接错误分析

导致符号未定义的原因:

1. 链接时缺少某个库, 目标文件的路径不正确, 符号

的声明与定义不一样,父类声明的虚函数,父或子类

都没有实现等.

2. C与C++的库之间兼容问题

3. 链接时库的顺序问题

Page 12: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

常见链接错误分析—C与C++之间兼容问题

--符号修饰与函数签名// plusplus_example.hint func(int);float func(float);class C {

int func(int);class C2 {

int func(int);};

};namespace N {

int func(int);class C {

int func(int);};

}

// cplusplus_example.cpp

#include "example.h"int func(int){};float func(float){};int C::func(int){};int C::C2::func(int){};namespace N {

int func(int){};int C::func(int){};

}

Page 13: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

函数签名 修饰后名称(符号名)

int func(int) _Z4funcifloat func(float) _Z4funcfint C::func(int) _ZN1C4funcEiint C::C2::func(int) _ZN1C2C24funcEiint N::func(int) _ZN1N4funcEiint N::C::func(int) _ZN1N1C4funcEi

GCC的基本C++名称修饰方法:1. 所有的符号都以"_Z"开头,

2. 对于嵌套的名字(在名称空间或在类里面的),后面紧

跟"N",3. 然后是各个名称空间和类的名字,每个名字前是名字字

符串长度,再以"E"结尾。

4. 函数来说,它的参数列表紧跟在"E"后面

常见链接错误分析—C与C++之间兼容问题

--符号修饰与函数签名

Page 14: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

常见链接错误分析—C与C++之间兼容问题

--符号修饰与函数签名

#ifdef __cplusplusextern "C" {#endif

void * memset(void *, int ,size_t);

#ifdef __cplusplus}#endif

extern "C" 关键字

条件宏 __cplusplus

Page 15: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

链接时库的顺序问题--libtimeutil

Page 16: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

链接时库的顺序问题--libtimeutil

Page 17: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

链接时库的顺序问题--libdso

Page 18: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例A(1)• case02:

• case03:

• Case04:

• Case05:

Page 19: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例A(2)

• case02:

• case03:

• case04:

• case05:

Page 20: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例B(1)

• Case06:

Page 21: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例B(2)

• Case07:

• Case08:

• Case09:

Page 22: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例B(3)• Case06:

• Case08:

• Case09:

• Case07:

Page 23: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例C(1)

• libdso:

Page 24: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例C(2)• Case10:

• Case14:

• Case11:

Page 25: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例C(3)

• Case16:

• Case17:

• Case15:

Page 26: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例C(4)• Case10:

• Case14:

• Case11:

Page 27: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例C(5)• Case15:

• Case17:

• Case16:

Page 28: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例D(1)• libaba:

Page 29: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例D(2)• Makefile

Page 30: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例D(3)• aba_main.cpp:

• Case20:

• Case21:

• Case22:

Page 31: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例D(4)• Case20:

• Case22:

• Case21:

Page 32: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—总结

• 在链接静态库时, 如果目标文件(.o) 与静态库之间存在依赖关系,

则有依赖关系的目标文件(.o)与静态库存在链接顺序问题。

• 在链接静态库时, 如果动态库 与静态库之间存在依赖关系,则有

依赖关系的动态库与静态库存在链接顺序问题。

• 在链接静态库时,如果多个静态库之间存在依赖关系,则有依

赖关系的静态库之间存在链接顺序问题。

• 静态库之间存在循环依赖的时候经常需要根据依赖关系,将静态

库列出多次.

• 遵守从左到右排序对象文件的惯例.

Page 33: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

静态链接

静态链接过程

符号解析

重定位过程

Page 34: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

静态链接—输入文件类型

• 重定位目标文件

• 归档库

• 共享库

Page 35: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

静态链接--处理过程

• 链接的主要内容是把各个模块之间相互引用的部

分处理好,使各个模块之间能正确的衔接.

• 两步链接(Two-passing Linking)

地址与空间分配(Address and Storage Allocation)

符号解析(Symbol Resolution)和重定位(Relocation)

• 全局符号表:链接器会保存一个全局符号表,在任

何输入文件中被引用或者定义的符号都会有一个

表项

Page 36: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

静态链接--地址与空间分配

Page 37: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

静态链接—符号分类与符号解析

• 符号分类

• 符号解析(symbol resolution)概念

Page 38: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

库的顺序问题—示例D

• Case22:

• Case24:

Page 39: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

静态链接—符号处理示例—case22

Page 40: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

静态链接—符号处理示例—case24

Page 41: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

静态链接时重定位

• 重定位概念

• 重定位表

• 符号解析与重定位的关系

Page 42: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

Elf64_Rela// Relocation table entry with addend (in section of type SHT_RELA).

typedef struct

{

Elf64_Addr r_offset; /* Address of need relocation*/

Elf64_Xword r_info; /* Relocation type and symbol index */

Elf64_Sxword r_addend; /* Addend */

} Elf64_Rela;

#define ELF64_R_SYM(i) ((i) >> 32) // 符号表中的下标

#define ELF64_R_TYPE(i) ((i) & 0xffffffff) // 重定位入口的类型

#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32)

+ (type))

Page 43: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

重定位类型表

名字 值 计算

R_X86_64_32 1 S+A

R_X86_64_PC32 2 S+A-P

R_X86_64_GOT32 3 G + A

R_X86_64_PLT32 4 L + A - P

R_X86_64_COPY 5 none

R_X86_64_GLOB_DAT 6 S

R_X86_64_JUMP_SLOT 7 S

R_X86_64_RELATIVE 8 B + A

R_X86_64_GOTPCREL 9 S + A - GOT

Page 44: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

静态链接时重定位示例

Page 45: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

静态链接时重定位示例

Page 46: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

动态库 与 动态链接基础

如何创建动态库

为什么要动态链接

动态链接的主要技术点

显式运行时加载(dlopen/dlsym/dlclose)

动态库连接与动态加载选项说明

Page 47: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

如何创建动态库

Page 48: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

为什么要动态链接

• 内存与磁盘空间

• 程序开发与发布

• 程序的可扩展性与兼容性

Page 49: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

动态链接

• 基本思想:把程序按

照模块分拆成各个

相对独立部分,在程

序运行时才将它们

链接在一起形成一

个单独的可执行文

件.

Page 50: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

动态链接的实现

• 方便程序开发,部属:

方法:程序的各模块独立,

装载时才链接(符号查找,

重定位).

• 节约内存:

方法: 地址无关代码(PIC)

技术实现:全局偏移量表

(GOT)

• 性能优化:

方法:延迟绑定(Lazy

Binding)

技术实现:函数链接表

(PLT)

• 程序的可扩展性与兼容性:

方法: 显式运行时加载

Page 51: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

动态链接的实现—GOT,PLT

Page 52: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

动态链接的实现—GOT,PLT

类型 2:模块内部的数据访问

类型 4:模块外部的数据访问

类型 1:模块内部的函数调用

类型 3:模块外部的函数调用

Page 53: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

动态链接的实现—GOT,PLT

Page 54: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

常用链接选项(1)

• -Wl:这个选项可以将指定的参数传递给链接器。

• -Bstatic : 将其后的库作为静态库链入.

• -Bdynamic: 将其后的库作为动态库链入.

• 示例: -Wl,-Bstatic -lanet -lalog -Wl,-Bdynamic –ldl

Page 55: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

ld 链接选项

• -Xlinker –rpath:

示例: -Xlinker -rpath ../timeutil

• --enable-new-dtags

示例: -Xlinker -rpath ../timeutil -Xlinker --enable-new-

dtags

• -export-dynamic 或 -rdynamic:

• -Bsymbolic:

示例: -Wl, -Bsymbolic

Page 56: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

动态链接过程—库的搜索路径

1. DT_RPATH: 在 .dynamic 节的 DT_RPATH的表项指定,

且没有 RUNPATH表项,是由分号分隔开的可以搜索库

的目录列表.

2. 环境变量 LD_LIBRARY_PATH

3. RUNPATH : 如果在 .dynamic 节有项,则DT_RPATH 项

失效.

4. 库缓冲文件/etc/ld.so.cache

5. 缺省目录: /usr/lib; /lib ; /usr/local/lib等

Page 57: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

分析工具

Page 58: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

binutls工具集

ld - the GNU linker.

as - the GNU assembler.

addr2line - Converts addresses into filenames and line numbers.

ar - A utility for creating, modifying and extracting from archives.

c++filt - Filter to demangle encoded C++ symbols.

gprof - Displays profiling information.

Page 59: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

binutls工具集 nm - Lists symbols from object files.

objcopy - Copys and translates object files.

objdump - Displays information from object files.

ranlib - Generates an index to the contents of an archive.

readelf - Displays information from any ELF format object file.

size - Lists the section sizes of an object or archive file.

strings - Lists printable strings from files.

strip - Discards symbols.

Page 60: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

参考资料

• <程序员的自我修养—链接、装载与库>

• <连接器和加载器>

Page 61: Linux c++ 编程之链接与装载 -基础篇--v0.3--20120509

谢 谢 !