linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

77
五五 五五五五五五 Linux C++ 编编编编编编编编 -- 编编编

Upload: tidesq

Post on 23-Jun-2015

9.844 views

Category:

Technology


0 download

DESCRIPTION

Linux c++ 编程之链接与装载 -提高篇:1).链接与装载基础篇--复习 2). 动态链接--主要实现技术,动态链接过程,全局符号介入问题分析,C++全局变量的构造与析构实现及相关问题分析 3). ELF 文件结构概述

TRANSCRIPT

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

五竹

一淘搜索与算法

Linux C++ 编程之链接与装载-- 提高篇

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

课程大纲页

• 链接与装载基础篇 -- 复习

• 动态链接

• ELF 文件结构概述

• 参考资料

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

链接与装载基础篇 -- 复习基本概念

静态链接过程与符号解析

静态链接时重定位

常见链接错误

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

编译与链接的基本概念 (1)

//hello.c

#include <stdio.h>

int main()

{

printf("Hello World\n");

return 0;

}

编译 :

gcc hello.c

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

编译与链接的基本概念 (2)

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

编译与链接的基本概念 (3)

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

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

• 链接的主要内容是把各个模块之间相互引用的部分处理好 , 使各个模块之间能正确的衔接 .

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

静态链接过程与符号解析—输入文件类型

• 重定位目标文件

• 归档库

• 共享库

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

静态链接过程

• 链接的主要内容是把各个模块之间相互引用的部分处理好 , 使各个模块之间能正确的衔接 .

• 两步链接 (Two-passing Linking)

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

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

• 全局符号表 : 链接器会保存一个全局符号表,在任何输入文件中被引用或者定义的符号都会有一个表项

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

符号分类与符号解析

• 符号分类

• 符号解析 (symbol resolution) 概念

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

静态链接—符号处理示例• libaba:

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

静态链接—符号处理示例• Makefile

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

静态链接—符号解析示例• aba_main.cpp:

• Case22:

• Case24:

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

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

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

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

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

静态链接时重定位

• 重定位概念

• 重定位表

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

Page 16: 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 17: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

重定位类型表

名字 值 计算R_X86_64_32 1 S+AR_X86_64_PC32 2 S+A-PR_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 18: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

静态链接时重定位示例

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

静态链接时重定位示例

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

常见链接错误导致符号未定义的原因 :

1. 链接时缺少某个库 , 目标文件的路径不正确 , 符号的声明与定义不一样 , 父类声明的虚函数 , 父或子类都没有实现等 .

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

3. 链接时库的顺序不正确

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

常见链接错误 -- 库的顺序问总结

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

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

• 在链接静态库时 , 如果动态库 与静态库之间存在依赖关系 , 则有依赖关系的动态库与静态库存在链接顺序问题。

• 在链接静态库时,如果多个静态库之间存在依赖关系,则有依赖关系的静态库之间存在链接顺序问题。

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

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

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

动态链接

主要实现技术

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

动态链接过程

符号的优先级与全局符号介入

C++ 全局变量的构造与析构

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

动态链接的主要实现技术• 方便程序开发 , 部属 :

方法 : 程序的各模块独立 , 装载时才链接 ( 符号查找 , 重定位 ).

• 节约内存 :

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

技术实现 : 全局偏移量表 (GOT)

• 性能优化 :

方法 : 延迟绑定 (Lazy

Binding)

技术实现 : 函数链接表(PLT)

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

方法 : 显式运行时加载

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

主要实现技术—相关数据结构

• .interp 节 : 动态链接器 ld.so• .dynamic 节 : 保存了动态链接器所需的基本信息 ,• 动态符号表 :

.dynsym: 只保存与动态链接相关的符号 ; .dynstr: 动态符号字符串表 : .hash .gnu.hash: 输助的符号哈唏表

• 动态链接相关的重定位表 : .rela.plt : 对数据引用的修正 , 它所修正的位

置位于 ".got" 和数据段 . .rela.dyn : 对函数引用的修正 , 它所修正的位

置位于 ".got.plt"

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

动态链接的实现 -- 地址无关代码

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

动态链接的实现 -- 地址无关代码

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

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

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

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

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

The Procedure Linkage Table

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

模块外部的访问与延迟绑定

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

显式运行时加载#include <dlfcn.h>void *dlopen(const char *filename, int flag);功能 : 指定模式打开指定的动态连接库文件,并返

回一个句柄给调用进程 .Flags 取值:

RTLD_LAZY: 延迟绑定,等有需要时再解出符号RTLD_NOW: 立即绑定,返回前解除所有未决定的符号。RTLD_LOCAL: 与 RTLD_GLOBAL 作用相反,动态库中定

义的符号不能被其后打开的其它库重定位。没有指定 ,则缺省为 RTLD_LOCAL 。

RTLD_GLOBAL 动态库中定义的符号可被其后打开的其它库重定位。

RTLD_DEEPBIND: 符号查找优先在本共享库的符号表 ,找不到才去全局符号表中查找

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

显式运行时加载

void *dlsym(void *handle, const char *symbol);• 功能:返回指定符号的地址• handle 取值 :

RTLD_DEFAULT: 直接在可执行程序的全局符号表中查找

RTLD_NEXT: 在可执行程序的全局符号表中查找第 2 次出现的符号。

dlopen 返回的 handle

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

dlsym 之 RTLD_NEXT应用--Hook 内存函数 malloc

#include <stdio.h>

#include <stdlib.h>

#include <dlfcn.h>

void *malloc(size_t size) {

size_t *(*_malloc)(size_t size) = NULL;

_malloc = (size_t *(*)(size_t size))

dlsym(RTLD_NEXT, "malloc");

return NULL;

}

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

动态链接过程程序加载

动态链接器自举

处理环境变量

装载共享对象

重定位和初始化

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

动态链接过程—内存组织关系

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

动态链接过程—内存组织关系

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

动态链接过程—内存组织关系

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

符号的优先级与全局符号介入—示例 1

• tcmalloc 是怎么生效的 ?

• 静态库 : libtcmalloc.a ?

• 动态库 : libtcmalloc.so

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

符号的优先级与全局符号介入—示例 2• cmclient_v1

• cmclient_v2

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

符号的优先级与全局符号介入—示例 2• dso

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

符号的优先级与全局符号介入—示例 2• case01: symbol_priority_dlopen

• case02: symbol_priority_dlopen_deepbind

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

符号的优先级与全局符号介入—示例 2• case03: symbol_priority_link

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

符号的优先级与全局符号介入—示例 2

• case03: symbol_priority_link

• case01: symbol_priority_dlopen

• case02: symbol_priority_dlopen_deepbind

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

C++ 全局变量的构造与析构—示例

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

C++ 全局变量的构造与析构—示例

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

C++ 全局变量的构造与析构—示例

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

C++ 全局变量的构造与析构—示例

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

C++ 全局变量的构造与析构— case01&case02

Case02:

Case01:

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

C++ 全局变量的构造与析构— case03

Case03:

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

C++ 全局变量的构造与析构—依赖关系 (1)

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

C++ 全局变量的构造与析构— case01

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

C++ 全局变量的构造与析构— case02

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

C++ 全局变量的构造与析构— case03

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

C++ 全局变量的构造与析构 --.ctors 节的形成

• g++ -o $@ $^ -L./ -Wl,-Bstatic -lclass_a -Wl,-Bdynamic -ldl

--verbose

/usr/libexec/gcc/x86_64-redhat-linux/4.1.2/collect2 --eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o main /usr/lib64/crt1.o /usr/lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtbegin.o -L./ -L/usr/lib/gcc/x86_64-redhat-linux/4.1.2 -L/usr/lib/gcc/x86_64-redhat-linux/4.1.2 -L/usr/lib64 -L/lib64 -L/usr//lib64 main.o -Bstatic -lclass_a -Bdynamic -ldl -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-redhat-linux/4.1.2/crtend.o /usr/lib64/crtn.o

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

C++ 全局变量的构造与析构 --.ctors 节的形成

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

C++ 全局变量的构造与析构

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

C++ 全局变量的构造与析构

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

C++ 全局变量的构造与析构— case01

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

C++ 全局变量的构造与析构— case02

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

C++ 全局变量的构造与析构—示例

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

C++ 全局变量的构造与析构—依赖关系 (2)

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

C++ 全局变量的构造与析构— case04&case05

Case05:

Case04:

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

C++ 全局变量的构造与析构— case04

Page 62: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

C++ 全局变量的构造与析构— case05

Page 63: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

C++ 全局变量的构造与析构— case05

Page 64: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

C++ 全局变量的构造与析构— case06&case07

Case07:

Case06:

Page 65: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

C++ 全局变量的构造与析构— case06

Page 66: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

C++ 全局变量的构造与析构— case07

Page 67: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

ELF 文件结构概述

Page 68: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

ELF 文件结构概述int printf(const char * format,...);int global_init_var=84;int global_uninit_var;void func1(int i ){ printf("%d\n",i);}int main(void){ static int static_var=85; static int static_var2; int a=1; int b; func1(static_var+static_var2+a+b); return a;}

Page 69: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

ELF 文件结构概述

Page 70: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

ELF 文件结构概述名称 说明

文件头 位于目标文件最开始的位置,含有整个文件的一些基本信息 .

程序头表 一个可执行文件或共享目标文件的程序头表是一个数组,数组中的每一个元素称为“程序头” . 描述了一个“ 段 (segment)” 或者一块用于准备执行程序的信息。

节头表 是一个数组 , 每一项是一个 Elf32(64)_Shdr 结构,通过每一个表项可以定位到对应的节。

.strtab 表 字符串表包含有若干个以’ null’ 结尾的字符序列,即字符串 . 保存符号的名字或者节的名字

.symtab 表 符号表包含用来定位、重定位程序中符号定义和引用的信息。

重定位表 含如何修改其节区内容的信息,从而允许可执行文件和共享目标文件保存进程的程序映像的正确信息 . 静态链接 :.rela.text 和 .rela.data); 动态链接 :.rela.plt 和 .rela.dyn

Page 71: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

ELF 文件结构概述名称 说明

.dynstr 表 动态字符串表包含有若干个以’ null’ 结尾的字符序列,即字符串 . 保存动态符号的名字的名字

.dynsym 表 动态符号表包含的信息用于动态重定位程序中的符号定义和引用

.interp 节 动态链接器 , /lib64/ld-linux-x86-64.so.2

.dynamic 节 保存动态链接器所需要的基本信息 , 如依赖的 DSO, dynsym 表的位置 , 动态链接重定位表的位置等 .

全局偏移表( GOT )

全局偏移表在私有数据中包含绝对地址,从而使得地址可用,并且不会影响位置独立性和程序代码的可共享性。如果程序需要直接访问某个符号的绝对地址,该符号就会具有一个全局偏移表项 .重定位项的类型 R_386_GLOB_DAT ,表示对全局偏移表的引用

过程链接表( PLT )

过程链接表( PLT )能够把位置独立的函数调用重定向到绝对位置 .

哈希表( Hash Ta ble )

哈希表支持符号表访问

Page 72: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

ELF ‘Views’ – Split

Page 73: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

ELF Loading View

• Much simpler view, divides executable into ‘Segments’

• Only includes data to be loaded into memory at runtime

• Segments have:– A simple type– Requested memory location at run

time– Permissions

(readable/writeable/executable)

Page 74: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

ELF ‘Views’ - Linking to Loading

Page 75: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

ELF 文件结构概述

• 实际示例 (understanding_elf.rar):

• part_o.pdf

• main_s.pdf

Page 76: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

参考资料

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

• < 连接器和加载器 >

• <ELF_Format.pdf>

• http://blog.chinaunix.net/uid/725631/frmd/18945.

html

• Glibc 和 binutils 源码

Page 77: Linux c++ 编程之链接与装载 -提高篇--v0.3--20120509

谢 谢 !