nm工具可以用来查看符号信息;检索包含在目标文件里的符号;
objcopy:可以用来对段信息做修改;
od 工具格式化为字符串输出
ar工具 建立、修改、提取归档文件;归档文件是包含多个文件内容的一个大文件,其结构保证了可以恢复原始内容;
ldd命令可以查看模块依赖哪些共享库;
strings 显示文件中可能的字符串;
ltrace 可以跟踪库函数调用
strace 跟踪系统调用
addr2line 从地址获取文件名和行号;
|
od -t x1z -A x test_define | head -5
十六进制(字节显示), z : 输出ASC, 以十六基数显示下标;
|
|
~/arm-marvell-linux-uclibcgnueabi-readelf -S vmlinux //内核段信息
~/arm-marvell-linux-uclibcgnueabi-objdump vmlinux -ds -j .init.arch.info //查看某一个段具体内容
|
objdump -h XXX 各个段的基本信息;
objdump -x XXX
-s将段的内容以十六机制打印出来,
-d将所有包含指令的段反汇编;
.rodata 只读数据段;
.comment 注释信息段
.note.GNU-stack 堆栈提示段
CONTENTS, ALLOC,等表示段的各种属性;
有一个专门的命令size,来查看ELF文件的代码段、数据段、BSS段的长度;
$size XXX
|
|
|
|
常用段名
|
说明
|
|
.comment
|
存放是编译器版本信息,比如字符串:“GCC:(GNU)4.2.0”
|
|
.debug
|
调试信息
|
|
.dynamic
|
动态链接信息
|
|
.hash
|
符号哈希表
|
|
.line
|
调试时的行号表,即源代码行号与编译后指令的对应表
|
|
.strtab
|
String Table 字符串表,用于存储ELF文件中用到的各种字符串
|
|
.symtab
|
symbol table 符号表
|
|
.shsrtab
|
section string table 段名表
|
|
.plt
.got
|
动态链接的跳转表和全局入口表
|
|
.init
.fini
|
程序初始化和终结代码段
|
|
.note
|
额外的编译器信息,比如程序的公司名、发布版本号等;
|
若想将图片,MP3,词典一类的作为目标文件可以借助,objcopy工具;
|
[root@localhost code]$readelf -h test
ELF Header:
Magic: 7f 45 4c 46 01 01 01 03 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - Linux
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8048300
Start of program headers: 52 (bytes into file)
Start of section headers: 2176 (bytes into file) //段表的位置,位于 .shstrtab和.symtab之间;
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 8
Size of section headers: 40 (bytes)
Number of section headers: 30
Section header string table index: 27
|
段表: section header table
|
[root@localhost code]$readelf -S test
There are 30 section headers, starting at offset 0x880:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048134 000134 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048148 000148 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048168 000168 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0804818c 00018c 000020 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481ac 0001ac 000050 10 A 6 1 4
[ 6] .dynstr STRTAB 080481fc 0001fc 00004a 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048246 000246 00000a 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 08048250 000250 000020 00 A 6 1 4
[ 9] .rel.dyn REL 08048270 000270 000008 08 A 5 0 4
[10] .rel.plt REL 08048278 000278 000018 08 A 5 12 4
[11] .init PROGBITS 08048290 000290 000030 00 AX 0 0 4
[12] .plt PROGBITS 080482c0 0002c0 000040 04 AX 0 0 4
[13] .text PROGBITS 08048300 000300 00017c 00 AX 0 0 16
[14] .fini PROGBITS 0804847c 00047c 00001c 00 AX 0 0 4
[15] .rodata PROGBITS 08048498 000498 000019 00 A 0 0 4
[16] .eh_frame_hdr PROGBITS 080484b4 0004b4 00001c 00 A 0 0 4
[17] .eh_frame PROGBITS 080484d0 0004d0 000068 00 A 0 0 4
[18] .ctors PROGBITS 08049538 000538 000008 00 WA 0 0 4
[19] .dtors PROGBITS 08049540 000540 000008 00 WA 0 0 4
[20] .jcr PROGBITS 08049548 000548 000004 00 WA 0 0 4
[21] .dynamic DYNAMIC 0804954c 00054c 0000c8 08 WA 6 0 4
[22] .got PROGBITS 08049614 000614 000004 04 WA 0 0 4
[23] .got.plt PROGBITS 08049618 000618 000018 04 WA 0 0 4
[24] .data PROGBITS 08049630 000630 000018 00 WA 0 0 4
[25] .bss NOBITS 08049648 000648 000008 00 WA 0 0 4
[26] .comment PROGBITS 00000000 000648 00013b 00 0 0 1
[27] .shstrtab STRTAB 00000000 000783 0000fc 00 0 0 1
[28] .symtab SYMTAB 00000000 000d30 000430 10 29 46 4
[29] .strtab STRTAB 00000000 001160 000208 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
[root@localhost code]$readelf -h test
|
type 段类型:
PROGBITS 程序段、代码段、数据段
SYMTAB 表示该段内容为符号表
STRTAB 该段内容为字符串表;
RELA 重定位表;包含重定位信息;
HASH 符号表的hash表;
DYNAMIC 动态链接信息;
NOTE 提示信息;
NOBITS 表示该段在文件中没内容
REL 该段包含重定位信息;
SHLIB 保留
DYNSYM 动态链接的符号表
Lk \ Inf :
section address alignment 段地址对齐;
AI为0或1表示无对齐要求,若为4,表示对齐为2的4次方,即16对齐;
section entry size ES: 有些段包含了固定大小的项,由ES表示; 0表示不含固定大小项;
重定位表:
|
[ 5] .dynsym DYNSYM 080481ac 0001ac 000050 10 A 6 1 4
[ 6] .dynstr STRTAB 080481fc 0001fc 00004a 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048246 000246 00000a 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 08048250 000250 000020 00 A 6 1 4
[ 9] .rel.dyn REL 08048270 000270 000008 08 A 5 0 4
[10] .rel.plt REL 08048278 000278 000018 08 A 5 12 4
[12] .plt PROGBITS 080482c0 0002c0 000040 04 AX 0 0 4
|
.rel.plt Lk == 5, 表示符号表下标为.dynsym, Inf==12表示其作用的段为plt;
在链接中,将函数和变量称为符号 Symbol, 函数名和变量名就是符号名 Symbol name;
链接接口---符号
|
[root@localhost code]$readelf -s test
Symbol table '.dynsym' contains 5 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
2: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.0 (2)
3: 00000000 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.0 (2)
4: 0804849c 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
Symbol table '.symtab' contains 67 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 08048134 0 SECTION LOCAL DEFAULT 1
2: 08048148 0 SECTION LOCAL DEFAULT 2
......
25: 08049648 0 SECTION LOCAL DEFAULT 25
26: 00000000 0 SECTION LOCAL DEFAULT 26
27: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
28: 08049538 0 OBJECT LOCAL DEFAULT 18 __CTOR_LIST__
29: 08049540 0 OBJECT LOCAL DEFAULT 19 __DTOR_LIST__
30: 08049548 0 OBJECT LOCAL DEFAULT 20 __JCR_LIST__
31: 08048330 0 FUNC LOCAL DEFAULT 13 __do_global_dtors_aux
32: 08049648 1 OBJECT LOCAL DEFAULT 25 completed.5934
33: 0804964c 4 OBJECT LOCAL DEFAULT 25 dtor_idx.5936
34: 08048390 0 FUNC LOCAL DEFAULT 13 frame_dummy
35: 00000000 0 FILE LOCAL DEFAULT ABS crtstuff.c
36: 0804953c 0 OBJECT LOCAL DEFAULT 18 __CTOR_END__
37: 08048534 0 OBJECT LOCAL DEFAULT 17 __FRAME_END__
38: 08049548 0 OBJECT LOCAL DEFAULT 20 __JCR_END__
39: 08048450 0 FUNC LOCAL DEFAULT 13 __do_global_ctors_aux
40: 00000000 0 FILE LOCAL DEFAULT ABS test.c
41: 08049644 4 OBJECT LOCAL DEFAULT 24 i.2167
42: 08049618 0 OBJECT LOCAL HIDDEN 23 _GLOBAL_OFFSET_TABLE_
43: 08049538 0 NOTYPE LOCAL HIDDEN 18 __init_array_end
44: 08049538 0 NOTYPE LOCAL HIDDEN 18 __init_array_start
45: 0804954c 0 OBJECT LOCAL HIDDEN 21 _DYNAMIC
46: 08049630 0 NOTYPE WEAK DEFAULT 24 data_start
47: 080483e0 5 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
48: 08048300 0 FUNC GLOBAL DEFAULT 13 _start
49: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
50: 00000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
51: 08048498 4 OBJECT GLOBAL DEFAULT 15 _fp_hw
52: 0804847c 0 FUNC GLOBAL DEFAULT 14 _fini
53: 00000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@@GLIBC_
54: 08049634 13 OBJECT GLOBAL DEFAULT 24 String
55: 0804849c 4 OBJECT GLOBAL DEFAULT 15 _IO_stdin_used
56: 08049630 0 NOTYPE GLOBAL DEFAULT 24 __data_start
57: 080484a0 0 OBJECT GLOBAL HIDDEN 15 __dso_handle
58: 08049544 0 OBJECT GLOBAL HIDDEN 19 __DTOR_END__
59: 080483f0 90 FUNC GLOBAL DEFAULT 13 __libc_csu_init
60: 08049648 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
61: 08049650 0 NOTYPE GLOBAL DEFAULT ABS _end
62: 00000000 0 FUNC GLOBAL DEFAULT UND puts@@GLIBC_2.0
63: 08049648 0 NOTYPE GLOBAL DEFAULT ABS _edata
64: 0804844a 0 FUNC GLOBAL HIDDEN 13 __i686.get_pc_thunk.bx
65: 080483b4 36 FUNC GLOBAL DEFAULT 13 main
66: 08048290 0 FUNC GLOBAL DEFAULT 11 _init
|
Value: 符号地址,与符号类型有关,可能是一个绝对值,也能是地址;
Size:符号大小,对于包含数据的符号,这个值是数据类型的大小;若对应字符串,则是字符串大小;
Type:
NOTYPE: 未知
OBJECT: 是个数据对象,比如:变量或数组
FUNC: 函数或其他可执行代码
SECTION: 表示一个段;
FILE: 表示文件名,
Bind:
GLOBAL 全局符号,外部可见;
WEAK:弱引用
LOCAL:局部符号,外部不可见
Vis: 这个说C/C++没使用, 看来不是呃呃呃额额;
Ndx:若符号定义在本目标文件中,那么这个表示符号所在段在段表中的下标;还有一些特殊值;
ABS: 表示该符号包含了一个绝对值;
COMMON: 表示该符号是一个COMMON块类型的符号,一般来说,未初始化的全局符号定义就是这种类型的;
UNDEF:在本目标函数引用,但是定义在其他目标文件中;
Name
C++为了兼容C,在符号的管理上,C++有一个声明C的符号“extern “C” ”的用法;这样C++的名称修饰机制就不会起作用;
C语言不支持extern用法,为了避免使用两套头文件, C++定义了一个“__cplusplus”宏, C++编译器在编译C++程序时,默认定义这个宏,
弱符号/强符号
通过“__attribute__((weak))”来定义弱符号,
强引用/弱引用 “__attribute__((weakref))”
弱引用和弱符号主要用于库的链接过程;
DWARF: debug with arbitrary record format
VMA: virtual memory address
LMA: load memory address
编译器将未初始化的全局变量定义为弱符号;
编译器为什么不把未初始化的全局变量当做未初始化的局部变量 处理放在bss段中, 而是标记为COMMON类型的变量?
在编译单元 无法确定弱符号占用的空间,但在连接过程中弱符号占用空间就确定了,这时弱符号在最终输出文件的bss段为其分配空间;未初始化全局变量最终还是放在BSS段中。
ABI: application binary interface
影响ABI因素:硬件,编程语言,编译器,连接器,OS等;
对于C语言来说以下几个因素会影响ABI兼容:
- 内置类型大小和存储器中的放置方式
- 组合类型的存储方式和内存分布;
- 外部符号和用户定义符号之间的命名方式和解析方式;
- 函数调用方式:参数入栈顺序、返回值等;
- 堆栈分布,局部变量、参数在堆栈的位置,参数传递方式等;
- 寄存器使用约定;
4.5 静态库链接
使用ar工具可以看文件包含哪些目标文件
ar -t /lib/libc.a //t - display contents of archive 显示组成该文件的小文件
ar -x /lib/libc.a // x[o] - extract file(s) from the archive 解压出文件
ld连接器会自动寻找所需要的符号及它们所在的目标文件,将这些目标文件从libc.a中解压出来,最终将它们链接在一起成为一个可执行文件。
gcc 添加 --verbose可以将整个编译链接过程的中间步骤打印出来;
|
[lvjianlin@sw1-dev code]$ gcc -o test_define --verbose test_define.c
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/i686-redhat-linux/4.7.2/lto-wrapper
Target: i686-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl= --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --disable-build-with-cxx --disable-build-poststage1-with-cxx --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --enable-java-awt=gtk --disable-dssi --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre --enable-libgcj-multifile --enable-java-maintainer-mode --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib --with-ppl --with-cloog --with-tune=generic --with-arch=i686 --build=i686-redhat-linux
Thread model: posix
gcc version 4.7.2 20120921 (Red Hat 4.7.2-2) (GCC)
COLLECT_GCC_OPTIONS='-o' 'test_define' '-v' '-mtune=generic' '-march=i686'
/usr/libexec/gcc/i686-redhat-linux/4.7.2/cc1 -quiet -v test_define.c -quiet -dumpbase test_define.c -mtune=generic -march=i686 -auxbase test_define -version -o /tmp/ccRMluE4.s //这里cc1是gcc的编译器,生成临时文件/tmp/ccRMluE4.s
GNU C (GCC) version 4.7.2 20120921 (Red Hat 4.7.2-2) (i686-redhat-linux)
compiled by GNU C version 4.7.2 20120921 (Red Hat 4.7.2-2), GMP version 5.0.2, MPFR version 3.1.0, MPC version 0.9
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
ignoring nonexistent directory "/usr/lib/gcc/i686-redhat-linux/4.7.2/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/i686-redhat-linux/4.7.2/../../../../i686-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/i686-redhat-linux/4.7.2/include
/usr/local/include
/usr/include
End of search list.
GNU C (GCC) version 4.7.2 20120921 (Red Hat 4.7.2-2) (i686-redhat-linux)
compiled by GNU C version 4.7.2 20120921 (Red Hat 4.7.2-2), GMP version 5.0.2, MPFR version 3.1.0, MPC version 0.9
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 84b2ac02994b31f7c32f894822050a6a
COLLECT_GCC_OPTIONS='-o' 'test_define' '-v' '-mtune=generic' '-march=i686'
as -v --32 -o /tmp/ccNSHywD.o /tmp/ccRMluE4.s //as GNU汇编器,生成临时文件/tmp/ccNSHywD.o
GNU assembler version 2.22.52.0.1 (i686-redhat-linux) using BFD version version 2.22.52.0.1-10.fc17 20120131
COMPILER_PATH=/usr/libexec/gcc/i686-redhat-linux/4.7.2/:/usr/libexec/gcc/i686-redhat-linux/4.7.2/:/usr/libexec/gcc/i686-redhat-linux/:/usr/lib/gcc/i686-redhat-linux/4.7.2/:/usr/lib/gcc/i686-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/i686-redhat-linux/4.7.2/:/usr/lib/gcc/i686-redhat-linux/4.7.2/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-o' 'test_define' '-v' '-mtune=generic' '-march=i686'
/usr/libexec/gcc/i686-redhat-linux/4.7.2/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o test_define /usr/lib/gcc/i686-redhat-linux/4.7.2/../../../crt1.o /usr/lib/gcc/i686-redhat-linux/4.7.2/../../../crti.o /usr/lib/gcc/i686-redhat-linux/4.7.2/crtbegin.o -L/usr/lib/gcc/i686-redhat-linux/4.7.2 -L/usr/lib/gcc/i686-redhat-linux/4.7.2/../../.. /tmp/ccNSHywD.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i686-redhat-linux/4.7.2/crtend.o /usr/lib/gcc/i686-redhat-linux/4.7.2/../../../crtn.o //collect2是ld链接器的一个包装,调用ld来完成对目标文件的链接;
[lvjianlin@sw1-dev code]$
|
4.6 连接过程的控制
C++中的链接控制脚本叫做模块定义文件(Module-definition file),扩展名一般为.def.
Linux链接脚本扩展名: lds, ld script
默认链接脚本可以使用
ld --verbose来查看,存放在/usr/lib/ldscripts/下,
ld链接标本语法简介
“. = ”设置当前虚拟地址; 那么“.”就表示当前地址;
“/DISCARD/ :”被丢弃的段;
命令语句一般的格式是由一个关键字和紧跟其后的参数所组成。
|
命令语句
|
说明
|
|
ENTRY(symbol)
|
指定符号为入口地址,即进程执行的第一条用户空间的指令在进程地址空间的地址。
|
|
STARTUP(filename)
|
将文件filename作为连接过程中的第一个输入文件;
|
|
SEARCH_DIR(path)
|
将路径path加入到ld链接器的库查找目录,“-Lpath”
|
|
INPUT(file, file)
|
将指定文件作为链接过程中的输入文件;
|
|
INCLUDE filename
|
|
|
PROVIDE(symbol)
|
在连接脚本中定义某个符号
|
最复杂的就是SECTIONS段了,
*(.data) 所有输入文件的.data段;*为通配符,还可以使用?、 []等规则;
[a-z]*(.text*[A-Z]),表示输入文件中以小写字母a-z开头的文件中所有段名以.text开头,并且以大写字符A-Z结尾的段。
BFD库(binary file descriptor library)
PAE : physical address extension
由于可执行文件在装载时实际上是被映射的虚拟空间,所以可执行文件很多时候被叫做映射文件(Image)。
6.4.1 ELF文件链接视图和执行视图
内核关心段与装载有关的内容,不关心其段的实际内容;
为了避免段到内核的映射时产生的内存空间浪费,
ELF可执行文件引入了“segment”概念,一个segment包含一个或多个类似的section;segment是从装载的角度来划分ELF各个段;
从链接角度看,ELF文件是按 section存储的;
readelf有不同的命令来查看segment和section:
|
[lvjianlin@sw1-dev code]$ readelf -l test_define
Elf file type is EXEC (Executable file)
Entry point 0x8048300
There are 8 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x00100 0x00100 R E 0x4
INTERP 0x000134 0x08048134 0x08048134 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x08048000 0x08048000 0x00614 0x00614 R E 0x1000
LOAD 0x000614 0x08049614 0x08049614 0x00114 0x00118 RW 0x1000
DYNAMIC 0x000620 0x08049620 0x08049620 0x000e8 0x000e8 RW 0x4
NOTE 0x000148 0x08048148 0x08048148 0x00044 0x00044 R 0x4
GNU_EH_FRAME 0x000538 0x08048538 0x08048538 0x0002c 0x0002c R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
[lvjianlin@sw1-dev code]$
|
|
[lvjianlin@sw1-dev code]$ readelf -S test_define
There are 30 section headers, starting at offset 0x85c:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048134 000134 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048148 000148 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048168 000168 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0804818c 00018c 000020 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481ac 0001ac 000050 10 A 6 1 4
[ 6] .dynstr STRTAB 080481fc 0001fc 00004c 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048248 000248 00000a 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 08048254 000254 000020 00 A 6 1 4
[ 9] .rel.dyn REL 08048274 000274 000008 08 A 5 0 4
[10] .rel.plt REL 0804827c 00027c 000018 08 A 5 12 4
[11] .init PROGBITS 08048294 000294 000023 00 AX 0 0 4
[12] .plt PROGBITS 080482c0 0002c0 000040 04 AX 0 0 16
[13] .text PROGBITS 08048300 000300 0001c4 00 AX 0 0 16
[14] .fini PROGBITS 080484c4 0004c4 000014 00 AX 0 0 4
[15] .rodata PROGBITS 080484d8 0004d8 00005e 00 A 0 0 4
[16] .eh_frame_hdr PROGBITS 08048538 000538 00002c 00 A 0 0 4
[17] .eh_frame PROGBITS 08048564 000564 0000b0 00 A 0 0 4
[18] .init_array INIT_ARRAY 08049614 000614 000004 00 WA 0 0 4
[19] .fini_array FINI_ARRAY 08049618 000618 000004 00 WA 0 0 4
[20] .jcr PROGBITS 0804961c 00061c 000004 00 WA 0 0 4
[21] .dynamic DYNAMIC 08049620 000620 0000e8 08 WA 6 0 4
[22] .got PROGBITS 08049708 000708 000004 04 WA 0 0 4
[23] .got.plt PROGBITS 0804970c 00070c 000018 04 WA 0 0 4
[24] .data PROGBITS 08049724 000724 000004 00 WA 0 0 4
[25] .bss NOBITS 08049728 000728 000004 00 WA 0 0 4
[26] .comment PROGBITS 00000000 000728 00002c 01 MS 0 0 1
[27] .shstrtab STRTAB 00000000 000754 000106 00 0 0 1
[28] .symtab SYMTAB 00000000 000d0c 000420 10 29 44 4
[29] .strtab STRTAB 00000000 00112c 000258 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
|
ELF可执行文件/共享文件中, 描述segment的结构叫做程序头表(Program Header Table),
目标文件不需要被装载,所以没有程序头表;
.bss段合并到了.data中,这就导致了FileSiz MemSiz这个两个值有不一致的情况;
|
[lvjianlin@sw1-dev code]$ cat /proc/self/maps
08048000-08053000 r-xp 00000000 fd:01 144526 /usr/bin/cat
08053000-08054000 r--p 0000a000 fd:01 144526 /usr/bin/cat
08054000-08055000 rw-p 0000b000 fd:01 144526 /usr/bin/cat
0911f000-09140000 rw-p 00000000 00:00 0 [heap]
4a2bb000-4a2da000 r-xp 00000000 fd:01 159520 /usr/lib/ld-2.15.so
4a2da000-4a2db000 r--p 0001e000 fd:01 159520 /usr/lib/ld-2.15.so
4a2db000-4a2dc000 rw-p 0001f000 fd:01 159520 /usr/lib/ld-2.15.so
4a2de000-4a489000 r-xp 00000000 fd:01 168658 /usr/lib/libc-2.15.so
4a489000-4a48a000 ---p 001ab000 fd:01 168658 /usr/lib/libc-2.15.so
4a48a000-4a48c000 r--p 001ab000 fd:01 168658 /usr/lib/libc-2.15.so
4a48c000-4a48d000 rw-p 001ad000 fd:01 168658 /usr/lib/libc-2.15.so
4a48d000-4a490000 rw-p 00000000 00:00 0
b7585000-b7785000 r--p 00000000 fd:01 168630 /usr/lib/locale/locale-archive
b7785000-b7786000 rw-p 00000000 00:00 0
b779a000-b779b000 rw-p 00000000 00:00 0
b779b000-b779c000 r-xp 00000000 00:00 0 [vdso]
bfa0a000-bfa2b000 rw-p 00000000 00:00 0 [stack]
[lvjianlin@sw1-dev code]$
|
第三列表示偏移,表示VMA对应的segment在映像文件中的偏移;第四列是映像文件所在设备的主设备/次设备号;第五列表示映像文件的节点号。
head,stack主次设备号为0,没有映射到文件,这种VMA叫做匿名虚拟内存区域(Anonymous virtual memory area)
vdso:用于进程与内核进行通信使用;
动态链接
ELF动态链接文件称为动态共享对象(Dynamic Shared Objects),一般以.so为扩展名;
windows,称为动态链接库(Dynamical Linking Library),以.dll为扩展名;
glibc对应的库为 libc.so;
程序与libc.so之间的链接工作由动态链接器完成;
gcc -fPIC -shared -o lib.so lib.c
-shared产生共享对象,表示使用装载时重定位方法;
一个缺点是指令部分无法在多个进程之间共享, 目的是希望程序共享的指令部分在装载时不需要因为装载地址的改变而改变,所以实现的基本想法是把指令中那些需要被修改的部分分离出来,和数据部分放在一起;
这个技术被称为PIC(Position independent-code);
模块间的数据/函数访问需要用到GOT(Global Offset Table)全局偏移表;
7.3.4 共享模块的全局变量问题
多进程共享全局变量叫做共享数据段;
多个线程访问不同全局变量副本叫做“线程私有存储(Thread Local Storage)”;
数据段地址无关性
7.4 延迟绑定PLT
PLT: procedure Linkage Table;
7.5 动态链接相关结构
7.5.1 “.interp”段
这个是interpret (解释器)的缩写, 保存了动态链接器的路径;
|
objdump -s test_define > test_define.dump-1
cat test_define.dump-1
1
2 test_define: file format elf32-i386
3
4 Contents of section .interp:
5 8048134 2f6c6962 2f6c642d 6c696e75 782e736f /lib/ld-linux.so
6 8048144 2e3200 .2.
7 Contents of section .note.ABI-tag:
|
|
[lvjianlin@sw1-dev code]$ readelf -l test_define
Elf file type is EXEC (Executable file)
Entry point 0x8048300
There are 8 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x00100 0x00100 R E 0x4
INTERP 0x000134 0x08048134 0x08048134 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
|
7.5.2 .dynamic段
主要是动态链接器所需要的基本信息,比如依赖哪些共享对象,动态链接符号表的位置,动态链接重定位表的位置,共享对象初始化代码的地址等;
|
[lvjianlin@sw1-dev code]$ ldd test_define
linux-gate.so.1 => (0xb77de000) //内核虚拟共享对象,设计linux系统调用和内核
libc.so.6 => /lib/libc.so.6 (0x4a2de000)
/lib/ld-linux.so.2 (0x4a2bb000)
|
7.5.3 动态符号表
为了表示动态链接这些模块之间的符号导入导出关系,ELF专门有一个叫做动态符号表(dynamic symbol table)的段来保存这些信息;
7.5.4 动态链接重定位表
共享对象需要重定位的主要原因是导入符号的存在;
'.rel.dyn'是对数据引用的修正,所修正位置位于“.got”以及数据段;
'.rel.plt'是对函数引用的修正,所修正位置位于‘.got.plt';
动态链接器自举
具有一定条件的启动代码往往被称为自举;bootstrap;
重定位完成后,若共享对象存在 .init段,那么动态链接器就会执行 .init段中的代码,用以实现共享对象特有的初始化过程; 程序退出时执行.finit;
ELF动态链接器是glibc的一部分,它的源代码位于Glibc的elf目录下,实际入口位于sysdeps/i386/dl-machine.h中的__start;
_start调用了elf/rtld.c的_dl_start();
动态链接器本身是静态链接;
7.7 显示运行时链接
LD_LIBRARY_PATH
LD_PRELOAD
LD_DEBUG
LD_TRACE_LOADED_OBJECTS=1 设置该参数时,解释器将在执行程序之前检查必要的共享库,将其载入内存并把它的信息显示出来。
共享库构造和析构函数
"__attribute__((constructor(X)))" //X表示优先级
"__attribute__((destructor)) "
8.6.5 共享库脚本
将多个共享库合为一个; 也可以称为动态链接脚本;
Glibc入口函数
|
229
230 08048300 <_start>:
231 _start():
232 8048300: 31 ed xor %ebp,%ebp //清零
233 8048302: 5e pop %esi
234 8048303: 89 e1 mov %esp,%ecx //取出argc
235 8048305: 83 e4 f0 and $0xfffffff0,%esp
236 8048308: 50 push %eax
237 8048309: 54 push %esp
238 804830a: 52 push %edx
239 804830b: 68 c0 84 04 08 push $0x80484c0
240 8048310: 68 50 84 04 08 push $0x8048450
241 8048315: 51 push %ecx
242 8048316: 56 push %esi
243 8048317: 68 00 84 04 08 push $0x8048400
244 804831c: e8 cf ff ff ff call 80482f0 <__libc_start_main@plt>
245 8048321: f4 hlt
246 8048322: 66 90 xchg %ax,%ax
247
|
调用__start之前,装载器已经将用户的参数和环境变量压入栈中,
C语言运行时库, CRT;
TLS: Thread Local Storage 线程局部存储
#define error (*__errno_location())
不同线程调用__errno_location返回的地址不同;
11.3.3 线程局部存储实现
定义前面加一个关键字 "__thread"
TEB: thread environment Block 线程环境块
12.2.3 Linux新型系统调用机制
VDSO: virtual Dynamic Shared Library 虚拟动态共享库;
|
[lvjianlin@sw1-dev code]$ cat /proc/1181/maps
08048000-08049000 r-xp 00000000 fd:02 5146582 /home/lvjianlin/tmp/code/test_define
08049000-0804a000 rw-p 00000000 fd:02 5146582 /home/lvjianlin/tmp/code/test_define
4a2bb000-4a2da000 r-xp 00000000 fd:01 159520 /usr/lib/ld-2.15.so
4a2da000-4a2db000 r--p 0001e000 fd:01 159520 /usr/lib/ld-2.15.so
4a2db000-4a2dc000 rw-p 0001f000 fd:01 159520 /usr/lib/ld-2.15.so
4a2de000-4a489000 r-xp 00000000 fd:01 168658 /usr/lib/libc-2.15.so
4a489000-4a48a000 ---p 001ab000 fd:01 168658 /usr/lib/libc-2.15.so
4a48a000-4a48c000 r--p 001ab000 fd:01 168658 /usr/lib/libc-2.15.so
4a48c000-4a48d000 rw-p 001ad000 fd:01 168658 /usr/lib/libc-2.15.so
4a48d000-4a490000 rw-p 00000000 00:00 0
b773f000-b7740000 rw-p 00000000 00:00 0
b7753000-b7755000 rw-p 00000000 00:00 0
b7755000-b7756000 r-xp 00000000 00:00 0 [vdso]
bfe49000-bfe6a000 rw-p 00000000 00:00 0 [stack]
[lvjianlin@sw1-dev code]$
[lvjianlin@sw1-dev code]$
[lvjianlin@sw1-dev code]$ ldd test_define
linux-gate.so.1 => (0xb77ce000)
libc.so.6 => /lib/libc.so.6 (0x4a2de000)
/lib/ld-linux.so.2 (0x4a2bb000)
|
书上说可以使用如下的命令导出内存镜像,但在本机上操作失败;
dd if=/proc/self/mem of=/tmp/linux-gate.dso bs=4096 skip=XXX(b7755 二进制) count=1
objdump可以从某一个地址开始反汇编;
objdump -d --start-address=0xffffe400 --stop-address=0xffffe408 linux-gate.dso