linux怎样load elf可执行文件<一> ---- static vs dynamic
在linux下面运行程序:
strace ./test_dynamic
execve("./test_dynamic", ["./test_dynamic"], [/* 20 vars */]) = 0
[ Process PID=3820 runs in 32 bit mode. ]
可以看到其实是调一个系统调用execve,具体这个系统调用是怎么来让程序运行起来的?
本系列会做一个很基础的介绍
static vs dynamic
我们知道gcc在build一个程序的时候,可以指定程序是static link的还是dynamic link的。
static link会让目标文件包含所有需要的libs。生成的目标elf不依赖于任何其他的dso。
dynamic link不会包含所依赖的libs到目标文件。需要其他的dso才能正确加载目标文件。
例子:test.c
#include <stdio.h>
#include <stdlib.h>
main()
{
printf("Hello World!\n");
}
static link command: gcc -m32 -static test.c -o test_static (生成32bit的静态执行程序)
dynamic link command: gcc -m32 test.c -lpthread -o test_dynamic (生成32bit的动态执行程序,并且指定依赖libpthread)
几个有用的工具:
file, readelf.
file 可以输出文件的大概的信息
$file test_static输出:
test_static: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.26, BuildID[sha1]=0x471a71e89449ba50d36c892687ad560e501bf5f4, not stripped
$file test_dynamic输出:
test_dynamic: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0xcb6a9acbb6b64dd47436874eb8e7b2bb22e6266c, not stripped
readelf 可以输出elf相关的信息。比如 readelf -e 输出elf header 信息:
$readelf -e test_static
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 - GNU
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x80481c0
Start of program headers: 52 (bytes into file)
Start of section headers: 549012 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 5
Size of section headers: 40 (bytes)
Number of section headers: 29
Section header string table index: 26
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .note.ABI-tag NOTE 080480d4 0000d4 000020 00 A 0 0 4
[ 2] .note.gnu.build-i NOTE 080480f4 0000f4 000024 00 A 0 0 4
[ 3] .rel.plt REL 08048118 000118 000028 08 A 0 5 4
[ 4] .init PROGBITS 08048140 000140 000026 00 AX 0 0 4
[ 5] .plt PROGBITS 08048170 000170 000050 00 AX 0 0 16
[ 6] .text PROGBITS 080481c0 0001c0 06654c 00 AX 0 0 16
[ 7] __libc_freeres_fn PROGBITS 080ae710 066710 000ac1 00 AX 0 0 16
[ 8] .fini PROGBITS 080af1d4 0671d4 000017 00 AX 0 0 4
[ 9] .rodata PROGBITS 080af200 067200 018a58 00 A 0 0 32
[10] __libc_atexit PROGBITS 080c7c58 07fc58 000004 00 A 0 0 4
[11] __libc_subfreeres PROGBITS 080c7c5c 07fc5c 00002c 00 A 0 0 4
[12] .eh_frame PROGBITS 080c7c88 07fc88 0059b0 00 A 0 0 4
[13] .gcc_except_table PROGBITS 080cd638 085638 000120 00 A 0 0 1
[14] .tdata PROGBITS 080ce758 085758 000010 00 WAT 0 0 4
[15] .tbss NOBITS 080ce768 085768 000018 00 WAT 0 0 4
[16] .init_array INIT_ARRAY 080ce768 085768 000008 00 WA 0 0 4
[17] .fini_array FINI_ARRAY 080ce770 085770 000008 00 WA 0 0 4
[18] .jcr PROGBITS 080ce778 085778 000004 00 WA 0 0 4
[19] .data.rel.ro PROGBITS 080ce77c 08577c 000030 00 WA 0 0 4
[20] .got PROGBITS 080ce7ac 0857ac 000008 04 WA 0 0 4
[21] .got.plt PROGBITS 080ce7b4 0857b4 000020 04 WA 0 0 4
[22] .data PROGBITS 080ce7e0 0857e0 000760 00 WA 0 0 32
[23] .bss NOBITS 080cef40 085f40 001b94 00 WA 0 0 32
[24] __libc_freeres_pt NOBITS 080d0ad4 085f40 000018 00 WA 0 0 4
[25] .comment PROGBITS 00000000 085f40 000038 01 MS 0 0 1
[26] .shstrtab STRTAB 00000000 085f78 00011a 00 0 0 1
[27] .symtab SYMTAB 00000000 08651c 007b60 10 28 830 4
[28] .strtab STRTAB 00000000 08e07c 007395 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)
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x85758 0x85758 R E 0x1000
LOAD 0x085758 0x080ce758 0x080ce758 0x007e8 0x02394 RW 0x1000
NOTE 0x0000d4 0x080480d4 0x080480d4 0x00044 0x00044 R 0x4
TLS 0x085758 0x080ce758 0x080ce758 0x00010 0x00028 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
Section to Segment mapping:
Segment Sections...
00 .note.ABI-tag .note.gnu.build-id .rel.plt .init .plt .text __libc_freeres_fn .fini .rodata __libc_atexit __libc_subfreeres .eh_frame .gcc_except_table
01 .tdata .init_array .fini_array .jcr .data.rel.ro .got .got.plt .data .bss __libc_freeres_ptrs
02 .note.ABI-tag .note.gnu.build-id
03 .tdata .tbss
04
$readelf -e test_dynamic
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8048360
Start of program headers: 52 (bytes into file)
Start of section headers: 2036 (bytes into file)
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: 31
Section header string table index: 28
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] .hash HASH 0804818c 00018c 00002c 04 A 6 0 4
[ 5] .gnu.hash GNU_HASH 080481b8 0001b8 000020 04 A 6 0 4
[ 6] .dynsym DYNSYM 080481d8 0001d8 000060 10 A 7 1 4
[ 7] .dynstr STRTAB 08048238 000238 00006e 00 A 0 0 1
[ 8] .gnu.version VERSYM 080482a6 0002a6 00000c 02 A 6 0 2
[ 9] .gnu.version_r VERNEED 080482b4 0002b4 000020 00 A 7 1 4
[10] .rel.dyn REL 080482d4 0002d4 000008 08 A 6 0 4
[11] .rel.plt REL 080482dc 0002dc 000018 08 A 6 13 4
[12] .init PROGBITS 080482f4 0002f4 000026 00 AX 0 0 4
[13] .plt PROGBITS 08048320 000320 000040 04 AX 0 0 16
[14] .text PROGBITS 08048360 000360 000180 00 AX 0 0 16
[15] .fini PROGBITS 080484e0 0004e0 000017 00 AX 0 0 4
[16] .rodata PROGBITS 080484f8 0004f8 000015 00 A 0 0 4
[17] .eh_frame_hdr PROGBITS 08048510 000510 00001c 00 A 0 0 4
[18] .eh_frame PROGBITS 0804852c 00052c 000060 00 A 0 0 4
[19] .init_array INIT_ARRAY 0804958c 00058c 000004 00 WA 0 0 4
[20] .fini_array FINI_ARRAY 08049590 000590 000004 00 WA 0 0 4
[21] .jcr PROGBITS 08049594 000594 000004 00 WA 0 0 4
[22] .dynamic DYNAMIC 08049598 000598 0000f8 08 WA 7 0 4
[23] .got PROGBITS 08049690 000690 000004 04 WA 0 0 4
[24] .got.plt PROGBITS 08049694 000694 000018 04 WA 0 0 4
[25] .data PROGBITS 080496ac 0006ac 000008 00 WA 0 0 4
[26] .bss NOBITS 080496b4 0006b4 000004 00 WA 0 0 4
[27] .comment PROGBITS 00000000 0006b4 000038 01 MS 0 0 1
[28] .shstrtab STRTAB 00000000 0006ec 000106 00 0 0 1
[29] .symtab SYMTAB 00000000 000ccc 000430 10 30 45 4
[30] .strtab STRTAB 00000000 0010fc 000250 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)
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 0x0058c 0x0058c R E 0x1000
LOAD 0x00058c 0x0804958c 0x0804958c 0x00128 0x0012c RW 0x1000
DYNAMIC 0x000598 0x08049598 0x08049598 0x000f8 0x000f8 RW 0x4
NOTE 0x000148 0x08048148 0x08048148 0x00044 0x00044 R 0x4
GNU_EH_FRAME 0x000510 0x08048510 0x08048510 0x0001c 0x0001c 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 .hash .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
这里面有一点要注意, dynamic link的有一个program header的item:
INTERP 0x000134 0x08048134 0x08048134 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
在static link里面是没有的。这个INTERP的意思就是说这个可执行elf是动态链接的,它需要一个loader (/lib/ld-linux.so.2)
来协助加载。
static link没有这个item,意味着static link不需要loader协助,可以被linux kernel直接加载。
Linux kernel在加载一个可执行elf时,会判断有没有这个INTERP,如果没有,那就是static linked,kernel会直接加载
执行,如果有,那就是dynamic linked,kernel会加载INTERP,然后有INTERP来协助加载。
INTERP 一般都是有libc实现。比如glibc就实现了ld-linux.so.2。 如果看android的话,android的bionic自己也实现了
一个loader: linker。
下次我们会介绍execve的具体实现。