連接 其實有點像咱們平常生活中的標準化、模塊化生產、咱們有一個能夠生產標準螺帽的生產線,就能夠生產不少個不一樣的螺帽,
只有須要螺帽,咱們就能夠經過連接的方式、去複製一個出來,放到須要的點,大道汽車、小到信箱sass
可是、如咱們有不少個程序都要經過裝載器裝載到內存的裏面,那裏面連接好的一樣的功能代碼,也須要再裝載一遍、再佔一遍內存空間。服務器
這就比如,假設每一個人有騎自行車的需求,那咱們給每一個人生產一輛自行車帶在身邊,當然你們都有自行車用,可是馬路上確定會特別擁擠模塊化
一、首先lib.h定義了動態連接庫的一個函數show_me_the_money函數
[root@luoahong 10]# cat lib.h #ifndef LIB_H #define LIB_H void show_me_the_money(int money); #endif
二、lib.c包含了lib.h的實際實現spa
[root@luoahong 10]# cat lib.c #include <stdio.h> void show_me_the_money(int money) { printf("Show me USD %d from lib.c \n", money); }
三、而後show_me_poor.c調用了lib裏面的函數3d
[root@luoahong 10]# cat show_me_poor.c #include "lib.h" int main() { int money = 5; show_me_the_money(money); }
四、最後,咱們把lib.c變異成一個動態連接庫,也就是.so文件指針
[root@luoahong 10]# gcc lib.c -fPIC -shared -o lib.so [root@luoahong 10]# gcc -o show_me_poor show_me_poor.c ./lib.so
你能夠看到,在編譯的過程當中,咱們制定了一個-fPIC的參數。這個參數其實就是Position Independent Code 的意思,也就是咱們要把這個編譯成一個地址無關代碼。orm
而後。咱們再經過gcc編譯show_me_poor動態連接了lib.so的可執行文件,在這些操做走完成了以後,咱們把show_me_poor這個文件經過objdump出來看一下blog
…… 0000000000400540 <show_me_the_money@plt-0x10>: 400540: ff 35 12 05 20 00 push QWORD PTR [rip+0x200512] # 600a58 <_GLOBAL_OFFSET_TABLE_+0x8> 400546: ff 25 14 05 20 00 jmp QWORD PTR [rip+0x200514] # 600a60 <_GLOBAL_OFFSET_TABLE_+0x10> 40054c: 0f 1f 40 00 nop DWORD PTR [rax+0x0] 0000000000400550 <show_me_the_money@plt>: 400550: ff 25 12 05 20 00 jmp QWORD PTR [rip+0x200512] # 600a68 <_GLOBAL_OFFSET_TABLE_+0x18> 400556: 68 00 00 00 00 push 0x0 40055b: e9 e0 ff ff ff jmp 400540 <_init+0x28> …… 0000000000400676 <main>: 400676: 55 push rbp 400677: 48 89 e5 mov rbp,rsp 40067a: 48 83 ec 10 sub rsp,0x10 40067e: c7 45 fc 05 00 00 00 mov DWORD PTR [rbp-0x4],0x5 400685: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 400688: 89 c7 mov edi,eax 40068a: e8 c1 fe ff ff call 400550 <show_me_the_money@plt> 40068f: c9 leave 400690: c3 ret 400691: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0] 400698: 00 00 00 40069b: 0f 1f 44 00 00 nop DWORD PTR [rax+rax*1+0x0] ……
完整代碼進程
[root@luoahong 10]# objdump -d -M intel -S show_me_poor show_me_poor: file format elf64-x86-64 Disassembly of section .init: 00000000004004a0 <_init>: 4004a0: 48 83 ec 08 sub rsp,0x8 4004a4: 48 8b 05 4d 0b 20 00 mov rax,QWORD PTR [rip+0x200b4d] # 600ff8 <__gmon_start__> 4004ab: 48 85 c0 test rax,rax 4004ae: 74 05 je 4004b5 <_init+0x15> 4004b0: e8 3b 00 00 00 call 4004f0 <.plt.got> 4004b5: 48 83 c4 08 add rsp,0x8 4004b9: c3 ret Disassembly of section .plt: 00000000004004c0 <.plt>: 4004c0: ff 35 42 0b 20 00 push QWORD PTR [rip+0x200b42] # 601008 <_GLOBAL_OFFSET_TABLE_+0x8> 4004c6: ff 25 44 0b 20 00 jmp QWORD PTR [rip+0x200b44] # 601010 <_GLOBAL_OFFSET_TABLE_+0x10> 4004cc: 0f 1f 40 00 nop DWORD PTR [rax+0x0] 00000000004004d0 <show_me_the_money@plt>: 4004d0: ff 25 42 0b 20 00 jmp QWORD PTR [rip+0x200b42] # 601018 <show_me_the_money> 4004d6: 68 00 00 00 00 push 0x0 4004db: e9 e0 ff ff ff jmp 4004c0 <.plt> 00000000004004e0 <__libc_start_main@plt>: 4004e0: ff 25 3a 0b 20 00 jmp QWORD PTR [rip+0x200b3a] # 601020 <__libc_start_main@GLIBC_2.2.5> 4004e6: 68 01 00 00 00 push 0x1 4004eb: e9 d0 ff ff ff jmp 4004c0 <.plt> Disassembly of section .plt.got: 00000000004004f0 <.plt.got>: 4004f0: ff 25 02 0b 20 00 jmp QWORD PTR [rip+0x200b02] # 600ff8 <__gmon_start__> 4004f6: 66 90 xchg ax,ax Disassembly of section .text: 0000000000400500 <_start>: 400500: 31 ed xor ebp,ebp 400502: 49 89 d1 mov r9,rdx 400505: 5e pop rsi 400506: 48 89 e2 mov rdx,rsp 400509: 48 83 e4 f0 and rsp,0xfffffffffffffff0 40050d: 50 push rax 40050e: 54 push rsp 40050f: 49 c7 c0 80 06 40 00 mov r8,0x400680 400516: 48 c7 c1 10 06 40 00 mov rcx,0x400610 40051d: 48 c7 c7 ed 05 40 00 mov rdi,0x4005ed 400524: e8 b7 ff ff ff call 4004e0 <__libc_start_main@plt> 400529: f4 hlt 40052a: 66 0f 1f 44 00 00 nop WORD PTR [rax+rax*1+0x0] 0000000000400530 <deregister_tm_clones>: 400530: b8 37 10 60 00 mov eax,0x601037 400535: 55 push rbp 400536: 48 2d 30 10 60 00 sub rax,0x601030 40053c: 48 83 f8 0e cmp rax,0xe 400540: 48 89 e5 mov rbp,rsp 400543: 77 02 ja 400547 <deregister_tm_clones+0x17> 400545: 5d pop rbp 400546: c3 ret 400547: b8 00 00 00 00 mov eax,0x0 40054c: 48 85 c0 test rax,rax 40054f: 74 f4 je 400545 <deregister_tm_clones+0x15> 400551: 5d pop rbp 400552: bf 30 10 60 00 mov edi,0x601030 400557: ff e0 jmp rax 400559: 0f 1f 80 00 00 00 00 nop DWORD PTR [rax+0x0] 0000000000400560 <register_tm_clones>: 400560: b8 30 10 60 00 mov eax,0x601030 400565: 55 push rbp 400566: 48 2d 30 10 60 00 sub rax,0x601030 40056c: 48 c1 f8 03 sar rax,0x3 400570: 48 89 e5 mov rbp,rsp 400573: 48 89 c2 mov rdx,rax 400576: 48 c1 ea 3f shr rdx,0x3f 40057a: 48 01 d0 add rax,rdx 40057d: 48 d1 f8 sar rax,1 400580: 75 02 jne 400584 <register_tm_clones+0x24> 400582: 5d pop rbp 400583: c3 ret 400584: ba 00 00 00 00 mov edx,0x0 400589: 48 85 d2 test rdx,rdx 40058c: 74 f4 je 400582 <register_tm_clones+0x22> 40058e: 5d pop rbp 40058f: 48 89 c6 mov rsi,rax 400592: bf 30 10 60 00 mov edi,0x601030 400597: ff e2 jmp rdx 400599: 0f 1f 80 00 00 00 00 nop DWORD PTR [rax+0x0] 00000000004005a0 <__do_global_dtors_aux>: 4005a0: 80 3d 85 0a 20 00 00 cmp BYTE PTR [rip+0x200a85],0x0 # 60102c <_edata> 4005a7: 75 11 jne 4005ba <__do_global_dtors_aux+0x1a> 4005a9: 55 push rbp 4005aa: 48 89 e5 mov rbp,rsp 4005ad: e8 7e ff ff ff call 400530 <deregister_tm_clones> 4005b2: 5d pop rbp 4005b3: c6 05 72 0a 20 00 01 mov BYTE PTR [rip+0x200a72],0x1 # 60102c <_edata> 4005ba: f3 c3 repz ret 4005bc: 0f 1f 40 00 nop DWORD PTR [rax+0x0] 00000000004005c0 <frame_dummy>: 4005c0: 48 83 3d 48 08 20 00 cmp QWORD PTR [rip+0x200848],0x0 # 600e10 <__JCR_END__> 4005c7: 00 4005c8: 74 1e je 4005e8 <frame_dummy+0x28> 4005ca: b8 00 00 00 00 mov eax,0x0 4005cf: 48 85 c0 test rax,rax 4005d2: 74 14 je 4005e8 <frame_dummy+0x28> 4005d4: 55 push rbp 4005d5: bf 10 0e 60 00 mov edi,0x600e10 4005da: 48 89 e5 mov rbp,rsp 4005dd: ff d0 call rax 4005df: 5d pop rbp 4005e0: e9 7b ff ff ff jmp 400560 <register_tm_clones> 4005e5: 0f 1f 00 nop DWORD PTR [rax] 4005e8: e9 73 ff ff ff jmp 400560 <register_tm_clones> 00000000004005ed <main>: 4005ed: 55 push rbp 4005ee: 48 89 e5 mov rbp,rsp 4005f1: 48 83 ec 10 sub rsp,0x10 4005f5: c7 45 fc 05 00 00 00 mov DWORD PTR [rbp-0x4],0x5 4005fc: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] 4005ff: 89 c7 mov edi,eax 400601: e8 ca fe ff ff call 4004d0 <show_me_the_money@plt> 400606: c9 leave 400607: c3 ret 400608: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0] 40060f: 00 0000000000400610 <__libc_csu_init>: 400610: 41 57 push r15 400612: 41 89 ff mov r15d,edi 400615: 41 56 push r14 400617: 49 89 f6 mov r14,rsi 40061a: 41 55 push r13 40061c: 49 89 d5 mov r13,rdx 40061f: 41 54 push r12 400621: 4c 8d 25 d8 07 20 00 lea r12,[rip+0x2007d8] # 600e00 <__frame_dummy_init_array_entry> 400628: 55 push rbp 400629: 48 8d 2d d8 07 20 00 lea rbp,[rip+0x2007d8] # 600e08 <__init_array_end> 400630: 53 push rbx 400631: 4c 29 e5 sub rbp,r12 400634: 31 db xor ebx,ebx 400636: 48 c1 fd 03 sar rbp,0x3 40063a: 48 83 ec 08 sub rsp,0x8 40063e: e8 5d fe ff ff call 4004a0 <_init> 400643: 48 85 ed test rbp,rbp 400646: 74 1e je 400666 <__libc_csu_init+0x56> 400648: 0f 1f 84 00 00 00 00 nop DWORD PTR [rax+rax*1+0x0] 40064f: 00 400650: 4c 89 ea mov rdx,r13 400653: 4c 89 f6 mov rsi,r14 400656: 44 89 ff mov edi,r15d 400659: 41 ff 14 dc call QWORD PTR [r12+rbx*8] 40065d: 48 83 c3 01 add rbx,0x1 400661: 48 39 eb cmp rbx,rbp 400664: 75 ea jne 400650 <__libc_csu_init+0x40> 400666: 48 83 c4 08 add rsp,0x8 40066a: 5b pop rbx 40066b: 5d pop rbp 40066c: 41 5c pop r12 40066e: 41 5d pop r13 400670: 41 5e pop r14 400672: 41 5f pop r15 400674: c3 ret 400675: 90 nop 400676: 66 2e 0f 1f 84 00 00 nop WORD PTR cs:[rax+rax*1+0x0] 40067d: 00 00 00 0000000000400680 <__libc_csu_fini>: 400680: f3 c3 repz ret Disassembly of section .fini: 0000000000400684 <_fini>: 400684: 48 83 ec 08 sub rsp,0x8 400688: 48 83 c4 08 add rsp,0x8 40068c: c3 ret
咱們只關心整個可執行文件中的一部份內容。你應該能夠看到,在main函數調用show_me_the_money的函數的時候,對應的代碼是這樣的:
call 400550 <show_me_the_money@plt>
這裏後面一個@plt的關鍵字,表明了咱們須要從PLT,也就是程序連接表裏面找要挑用的函數,對應的地址則是400550這個地址
那麼當咱們把目錄挪到上面的400550這個地址,你會看到裏面進行了一次跳轉,這個跳轉指定國的跳轉地址,你能夠在後面的註釋裏能夠看到
GLOBAL_OFFSET_TABLE+0x18。這裏的GLOBAL_OFFSET_TABLE,就是咱們接下來的要說的全局偏移表
400550: ff 25 12 05 20 00 jmp QWORD PTR [rip+0x200512] # 600a68 <_GLOBAL_OFFSET_TABLE_+0x18>
在動態連接對應的共享庫,咱們在共享庫的data section裏面,保存了一張全局偏移表,雖然數據部分是各個動態連接它的應用程序裏面各加載一份的。全部須要引用當前共享庫外部的地址的指令,
都會查詢GOT,來找到當前運行程序的虛擬內存裏的對應位置。而GOT表裏的數據,則是在咱們加載一個個共享庫的時候寫進去的
不一樣的進程,調用一樣的lib.so各自裏面指向最終加載的動態連接庫裏面的虛擬內存地址是不一樣的
這樣,雖然不一樣的程序調用的一樣的動態庫,各自的內存地址是獨立的,挑用的有都是同一個動態庫,可是不須要去修改動態庫裏面的代碼所使用的地址
而是各個程序各自維護好本身的GOT,可以找到對應的動態庫就行了
一、GOT表位於共享庫本身的數據段裏,GOT表在內存裏和對應的代碼位置之間的偏移量,始終是肯定的,這樣咱們的共享庫是地址無關的代碼,
二、對應的各個程序只須要在物理內存裏面加載同一份代碼,而咱們又要經過這個能夠執行程序在加載時,生成的各個不相同的GOT表,來找到它須要調用到的外部變量和函數的地址
這是一個典型的、不修改代碼、而是經過修改「地址數據「來進行關聯的辦法,它有點像咱們在C語言裏面用函數指針調用對應的函數,並非經過預先已經肯定好的函數名稱
來調用,而是利用當時它在內存裏面的動態地址來調用
這一講、咱們終於在靜態連接和程序裝載以後,利用動態連接把咱們的內存利用到了極致。一樣功能的代碼生成共享庫,咱們只要在內存裏面保留一份就行了,
這樣咱們不只可以作到代碼在開發階段的複用,也能作到代碼在運行階段的複用
實際上、在進行Linux下的程序開發的時候,咱們一直會用到各類各樣的動態連接庫,C語言的標準庫在1MB以上。咱們撰寫任何一個程序可能都須要用到這個庫,
常見的Linux服務器裏,/usr/bin下面就有成千上外個可執行文件。若是每個都把標準庫靜態連接進來的,幾GB乃至幾十GB的磁盤空間一會兒就用出去了。
若是咱們服務端的多進程應用要開上千個進程,幾GB的內存空間也會一會兒就用甩出去了,這個問題在過去計算機的內存較少的時候更佳顯著
經過動態連接這個方式,能夠說完全解決這個問題,就像共享單車同樣,若是仔細經營,是一個頗有社會價值的事情,可是若是粗暴地把它變成無限制地
複製生產,每一個人造一輛,只會在系統內知道大量無用的垃圾
過去05-09折五講理,咱們已經把程序怎麼從源碼變成指令、數據、並裝載到內存裏面,由CPU一條條執行下去的過程講完了,但願你有所收穫,對於一個程序,是怎麼跑起來的,有了一個初步的認識