深入淺出計算機組成原理學習筆記:第十講


一、為什么需要動態鏈接庫

1、鏈接在生活中的應用

鏈接 其實有點像我們日常生活中的標准化、模塊化生產、我們有一個可以生產標准螺帽的生產線,就可以生產很多個不同的螺帽,
只有需要螺帽,我們就可以通過鏈接的方式、去復制一個出來,放到需要的點,大道汽車、小到信箱

2、靜態鏈接的缺點

但是、如我們有很多個程序都要通過裝載器裝載到內存的里面,那里面鏈接好的同樣的功能代碼,也需要再裝載一遍、再占一遍內存空間。

這就好比,假設每個人有騎自行車的需求,那我們給每個人生產一輛自行車帶在身邊,固然大家都有自行車用,但是馬路上肯定會特別擁擠

二、鏈接可以分動、靜、共享運行升內存

1、內存不夠用

2、鏈接過程

3、圖解動態鏈接過程

三、地址無關很重要,相對地址解煩惱

1、地址無關

2、地址相關

3、動態共享庫無法做到地址無關

 

四、PLT 和 GOT,動態鏈接的解決方案

1、示例代碼

1、首先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

2、lib.c包含了lib.h的實際實現

[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);
}

3、然后show_me_poor.c調用了lib里面的函數

[root@luoahong 10]# cat show_me_poor.c
#include "lib.h"
int main()
{
    int money = 5;
    show_me_the_money(money);
}

4、最后,我們把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 的意思,也就是我們要把這個編譯成一個地址無關代碼。

然后。我們再通過gcc編譯show_me_poor動態鏈接了lib.so的可執行文件,在這些操作走完成了之后,我們把show_me_poor這個文件通過objdump出來看一下

……
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>

五、GOT全局偏移表

在動態鏈接對應的共享庫,我們在共享庫的data section里面,保存了一張全局偏移表,雖然數據部分是各個動態鏈接它的應用程序里面各加載一份的。所有需要引用當前共享庫外部的地址的指令,

都會查詢GOT,來找到當前運行程序的虛擬內存里的對應位置。而GOT表里的數據,則是在我們加載一個個共享庫的時候寫進去的

不同的進程,調用同樣的lib.so各自里面指向最終加載的動態鏈接庫里面的虛擬內存地址是不同的

這樣,雖然不同的程序調用的同樣的動態庫,各自的內存地址是獨立的,挑用的有都是同一個動態庫,但是不需要去修改動態庫里面的代碼所使用的地址

而是各個程序各自維護好自己的GOT,能夠找到對應的動態庫就好了

1、GOT表位於共享庫自己的數據段里,GOT表在內存里和對應的代碼位置之間的偏移量,始終是確定的,這樣我們的共享庫是地址無關的代碼,

2、對應的各個程序只需要在物理內存里面加載同一份代碼,而我們又要通過這個可以執行程序在加載時,生成的各個不相同的GOT表,來找到它需要調用到的外部變量和函數的地址

這是一個典型的、不修改代碼、而是通過修改“地址數據“來進行關聯的辦法,它有點像我們在C語言里面用函數指針調用對應的函數,並不是通過預先已經確定好的函數名稱

來調用,而是利用當時它在內存里面的動態地址來調用

六、總結延伸

這一講、我們終於在靜態鏈接和程序裝載之后,利用動態鏈接把我們的內存利用到了極致。同樣功能的代碼生成共享庫,我們只要在內存里面保留一份就好了,

這樣我們不僅能夠做到代碼在開發階段的復用,也能做到代碼在運行階段的復用

 

實際上、在進行Linux下的程序開發的時候,我們一直會用到各種各樣的動態鏈接庫,C語言的標准庫在1MB以上。我們撰寫任何一個程序可能都需要用到這個庫,

常見的Linux服務器里,/usr/bin下面就有成千上外個可執行文件。如果每一個都把標准庫靜態鏈接進來的,幾GB乃至幾十GB的磁盤空間一下子就用出去了。

如果我們服務端的多進程應用要開上千個進程,幾GB的內存空間也會一下子就用甩出去了,這個問題在過去計算機的內存較少的時候更佳顯著

通過動態鏈接這個方式,可以說徹底解決這個問題,就像共享單車一樣,如果仔細經營,是一個很有社會價值的事情,但是如果粗暴地把它變成無限制地

復制生產,每個人造一輛,只會在系統內知道大量無用的垃圾

 

過去05-09折五講理,我們已經把程序怎么從源碼變成指令、數據、並裝載到內存里面,由CPU一條條執行下去的過程講完了,希望你有所收獲,對於一個程序,是怎么跑起來的,有了一個初步的認識


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM