?;厮菁夹g(shù)及uClibc的堆實(shí)現(xiàn)原理_第1頁(yè)
棧回溯技術(shù)及uClibc的堆實(shí)現(xiàn)原理_第2頁(yè)
?;厮菁夹g(shù)及uClibc的堆實(shí)現(xiàn)原理_第3頁(yè)
?;厮菁夹g(shù)及uClibc的堆實(shí)現(xiàn)原理_第4頁(yè)
棧回溯技術(shù)及uClibc的堆實(shí)現(xiàn)原理_第5頁(yè)
已閱讀5頁(yè),還剩16頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、?;厮菁夹g(shù)及uClibc的堆實(shí)現(xiàn)原理【摘要】本文描述棧的作用、uClibc上堆的實(shí)現(xiàn),利用?;厮菁夹g(shù)查找編程中經(jīng)常發(fā)生的段錯(cuò)誤問(wèn)題,理解棧、堆的作用,通過(guò)幾個(gè)例子分析越界訪問(wèn)導(dǎo)致的錯(cuò)誤?!娟P(guān)鍵詞】堆 棧 回溯 堆實(shí)現(xiàn) 棧作用一、問(wèn)題的提出段錯(cuò)誤、非法地址訪問(wèn)等問(wèn)題導(dǎo)致程序崩潰的現(xiàn)象屢屢發(fā)生,如果能找到發(fā)生錯(cuò)誤的函數(shù),往往一眼就能看出BUG所在對(duì)于這類比較簡(jiǎn)單的問(wèn)題,比如使用空指針進(jìn)行讀寫等,利用?;厮菁夹g(shù)可以很快定位。但是對(duì)于數(shù)組溢出、內(nèi)存泄漏等問(wèn)題導(dǎo)致的程序錯(cuò)誤,往往隱藏很深,它們并不當(dāng)場(chǎng)發(fā)作,即使我們一步一步跟蹤到發(fā)生錯(cuò)誤的語(yǔ)句時(shí),也經(jīng)常會(huì)讓人覺(jué)得“這個(gè)地方根本不可能出錯(cuò)啊”錯(cuò)誤在很早以前

2、就隱藏下來(lái)了,只不過(guò)是這個(gè)“不可能出錯(cuò)的語(yǔ)句”觸發(fā)了它。了解棧的作用、堆的實(shí)現(xiàn),可以讓我們腦中對(duì)程序的運(yùn)行、函數(shù)的調(diào)用、變量的操作有個(gè)感官的了解,對(duì)解決這類問(wèn)題會(huì)有所幫助。二、解決思路了解了棧,就可以通過(guò)?;厮菁夹g(shù)分析程序的調(diào)用關(guān)系,從而得出程序出錯(cuò)的流程;了解了堆,就可以對(duì)各類動(dòng)態(tài)分配、釋放內(nèi)存導(dǎo)致的錯(cuò)誤有個(gè)指導(dǎo)思想。(1)、棧的作用一個(gè)程序包含代碼段、數(shù)據(jù)段、BSS段、堆、棧;其中數(shù)據(jù)段用來(lái)中存儲(chǔ)初始值不為0的全局?jǐn)?shù)據(jù),BSS段用來(lái)存儲(chǔ)初始值為0的全局?jǐn)?shù)據(jù),堆用于動(dòng)態(tài)內(nèi)存分配,棧用于實(shí)現(xiàn)函數(shù)調(diào)用、存儲(chǔ)局部變量。比如對(duì)于如下程序:程序1 section.c01 #include 02 #in

3、clude 03 #include 04 05 int *g_pBuf;06 int g_iCount = 10;07 08 int main(int argc, char *argv)09 10 char str2;11 g_pBuf = malloc(g_iCount);12 printf(Address of main = 0x%08xn, (unsigned int)main);13 printf(Address of g_pBuf = 0x%08xn, (unsigned int)&g_pBuf);14 printf(Address of g_iCount = 0x%08xn, (u

4、nsigned int)&g_iCount);15 printf(Address of malloc buf = 0x%08xn, (unsigned int)g_pBuf);16 printf(Address of local buf str = 0x%08xn, (unsigned int)str);17 18 return 0;19 使用如下命令編譯得到可執(zhí)行文件section,反匯編文件section.dis:mips-uclibc-gcc -o section section.c staticmips-uclibc-objdump -D section section.dis在T50

5、0上的linux環(huán)境下,這個(gè)程序的輸出結(jié)果為:Address of main = 0x004000b0Address of g_pBuf = 0x100002d0Address of g_iCount = 0x10000000Address of malloc buf = 0x10002660Address of local buf = 0x7fff7e50其中main函數(shù)的地址為0x004000b0,它處于代碼段中;全局變量g_pBuf位于BSS段;全局變量g_iCount位于數(shù)據(jù)段;使用malloc分配出來(lái)的內(nèi)存地址為0x10002660,它位于堆中;局部變量str數(shù)組的開(kāi)始地址為0x7f

6、ff7e50,位于棧中。它們的分布圖示如下:圖1 程序各段示意圖棧的作用有二: 保存調(diào)用者的環(huán)境某些寄存器的值、返回地址 存儲(chǔ)局部變量現(xiàn)在通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明棧的作用:程序2 stack.c01 #include 02 #include 03 #include 0405 void A(int a);06 void B(int b);07 void C(int c);0809 void A(int a)10 11 printf(%d: A call Bn, a);12 B(2);13 1415 void B(int b)16 17 printf(%d: B call Cn, b);18 C(

7、3);19 2021 void C(int c)22 23 char *p = (char *)c;24 *p = A;25 printf(%d: function Cn, c);26 2728 int main(int argc, char *argv)29 30 char a;31 A(1);32 C(&a);33 return 0;34 使用如下命令編譯得到可執(zhí)行文件stack,反匯編文件stack.dis:mips-uclibc-gcc -o stack stack.c staticmips-uclibc-objdump -D stack stack .dis此程序的調(diào)用關(guān)系為main

8、 A B C,現(xiàn)在來(lái)看看棧如何變化:注意:1. 圖中“SP (32) = RA_after_xxx”表示SP+32的地方存放函數(shù)xxx執(zhí)行完后的返回地址2. 棧中不僅僅存儲(chǔ)返回地址,其它內(nèi)容沒(méi)標(biāo)出來(lái)圖2 函數(shù)調(diào)用中棧的變化上圖中,main、A、B、C四個(gè)函數(shù)的棧大小都是40字節(jié),返回地址都存在棧偏移地址為32的地方。我們是如何知道這點(diǎn)的呢?需要閱讀反匯編代碼:004000b0 : 4000b0:3c1c0fc1 luigp,0xfc1 4000b4:279c8090 addiugp,gp,-32624 4000b8:0399e021 addugp,gp,t9 4000bc:27bdffd8 a

9、ddiusp,sp,-40 4000c0:afbc0010 swgp,16(sp) 4000c4:afbf0020 swra,32(sp)00400128 : 400128:3c1c0fc1 luigp,0xfc1 40012c:279c8018 addiugp,gp,-32744 400130:0399e021 addugp,gp,t9 400134:27bdffd8 addiusp,sp,-40 400138:afbc0010 swgp,16(sp) 40013c:afbf0020 swra,32(sp)004001a0 : 4001a0:3c1c0fc0 luigp,0xfc0/ gp全

10、局指針,用來(lái)訪問(wèn)全局變量、函數(shù) 4001a4:279c7fa0 addiugp,gp,32672 4001a8:0399e021 addugp,gp,t9 4001ac:27bdffd0 addiusp,sp,-48 / 棧指針減48,這48字節(jié)的空間就是函數(shù)C的棧 4001b0:afbc0010 swgp,16(sp) / 在棧中保存gp 4001b4:afbf0028 swra,40(sp) / 在棧中保存返回地址ra 4001b8:afbe0024 sws8,36(sp) / 在棧中保存s8,此寄存器用來(lái)保存堆棧指針sp 4001bc:afbc0020 swgp,32(sp) / 又保存

11、一次gp,冗余 4001c0:03a0f021 moves8,sp / s8=sp,可見(jiàn)s8會(huì)被修改,所以先在上面保存原值 4001c4:afc40030 swa0,48(s8)/ a0用于傳遞參數(shù),對(duì)應(yīng)C語(yǔ)言,就是參數(shù)int c/ 把它保存在上一個(gè)函數(shù)的棧中 4001c8:8fc20030 lwv0,48(s8)/ v0=a0= int c 4001cc:00000000 nop 4001d0:afc20018 swv0,24(s8)/ 局部變量p 4001d4:8fc20018 lwv0,24(s8)/ 4001d8:24030041 liv1,65/ v1 = 65 = A 4001dc

12、:a0430000 sbv1,0(v0)/ 相當(dāng)于*p = A 4001e0:8f848018 lwa0,-32744(gp)/ 下面3條指令得到 4001e4:00000000 nop/ printf的第一個(gè)參數(shù)“%d: function Cn” 4001e8:24843230 addiua0,a0,12848 4001ec:8fc50030 lwa1,48(s8)/ printf的第二個(gè)參數(shù),顯然就是int c 4001f0:8f998110 lwt9,-32496(gp) 4001f4:00000000 nop 4001f8:0320f809 jalrt9/ t9為printf的地址,跳

13、轉(zhuǎn)執(zhí)行 4001fc:00000000 nop 400200:8fdc0010 lwgp,16(s8)/ 恢復(fù)gp 400204:03c0e821 movesp,s8 400208:8fbf0028 lwra,40(sp)/ 從棧中得到保存的返回地址 40020c:8fbe0024 lws8,36(sp)/ 從棧中得到保存的s8 400210:03e00008 jrra/ 跳轉(zhuǎn),返回(對(duì)于mips跳轉(zhuǎn)指令,執(zhí)行下一條指令后,才跳轉(zhuǎn)) 400214:27bd0030 addiusp,sp,48/ sp加上48,恢復(fù)棧指針00400218 : 400218:3c1c0fc0 luigp,0xfc0

14、 40021c:279c7f28 addiugp,gp,32552 400220:0399e021 addugp,gp,t9 400224:27bdffd0 addiusp,sp,-48 400228:afbc0010 swgp,16(sp) 40022c:afbf0028 swra,40(sp)上面紅色的指令“addiu sp,sp,-40”表示將SP寄存器的值減去40,也就意味著棧向下移動(dòng)40字節(jié)。指令“sw ra,32(sp)”表示將返回地址ra存放在地址(SP+32)的地方。局部變量也是存儲(chǔ)在棧中,當(dāng)一個(gè)函數(shù)的局部變量越多,它的棧越大。上面把函數(shù)C的反匯編代碼全部羅列出來(lái)了,現(xiàn)在以函數(shù)

15、C為例說(shuō)明調(diào)用一個(gè)函數(shù)時(shí),如何在棧中保存現(xiàn)場(chǎng)、如何存儲(chǔ)局部變量;參數(shù)如何傳遞、函數(shù)退出時(shí)如何恢復(fù)調(diào)用者現(xiàn)場(chǎng),然后返回。根據(jù)代碼注釋和圖3很容易理解:圖3 函數(shù)進(jìn)入、返回的棧變化棧中保存著函數(shù)的返回地址、局部變量等,那么我們可以從這些返回地址來(lái)確定函數(shù)的調(diào)用關(guān)系、調(diào)用順序。這就是下節(jié)介紹的?;厮荨?2)、?;厮萆厦娉绦?的第23、24兩行必然導(dǎo)致段錯(cuò)誤而使得程序崩潰,linux內(nèi)核當(dāng)發(fā)現(xiàn)發(fā)生段錯(cuò)誤時(shí),會(huì)打印出棧信息。我們可以使用?;厮莸姆椒ㄕ业桨l(fā)生錯(cuò)誤的原因。運(yùn)行結(jié)果stack程序,結(jié)果如下(注意:如果你是通過(guò)telnet來(lái)運(yùn)行程序,可以使用dmesg命令看到這些棧信息):/ # ./stack

16、 1: A call B2: B call Cdo_page_fault() #2: sending SIGSEGV to heap for illegal write access to00000003 (epc = 004001dc, ra = 00400188)$0 : 00000000 10006c00 00000003 00000041 00000003 0000000c 0000000c 00000000$8 : 00006c00 00000002 00000000 42203a32 19999999 00000000 00000057 00000115$16: 00000000

17、7fff7eb4 7fff7ebc 00000001 10005dc4 00000001 10005dbc 10005d94$24: 00000001 004001a0 10008140 7fff7db8 7fff7db8 00400188Hi : 00000002Lo : 00000000epc : 004001dc Not taintedStatus: 00006c13Cause : 3080000cProcess heap (pid: 70, stackpage=87876000)Stack: 7fff7ebc 00000001 10008140 0040050c 10008140 00

18、000000 00000003 00000000 10008140 7fff7de8 00400188 00400170 00000003 00000002 0000000c 00000000 10008140 65642f00 10008140 7fff7e10 00400110 004000f8 00000002 00000001 7fff7ebc 00000005 10008140 00000000 10008140 7fff7e38 00400258 7fff7eb4 00000001 10008140 00400368 00000000 10008140 00000000 00000

19、000 00000000 10008140 7fff7c68 0040046c 004003dc 00000001 7fff7eb4 00000000 00000000 10008140 00000000 10005dbc 10005da4 00000000 10005dc4 10008140 004002dc 00000000 00000000 00000000 00000000 00000000 00000000 00000001 7fff7f56 00000000 7fff7f60 7fff7f6a 7fff7f71 7fff7f7c 7fff7fde 7fff7fec 00000000

20、 00000010 00000000 00000006 00001000 00000011 00000064 00000003 00400034 00000004 00000020 00000005 00000003 00000007 00000000 00000008 00000000 00000009 00400290 0000000b 00000000 0000000c 00000000 0000000d 00000000 0000000e 00000000 00000000 00000000 00000000 00000000 00000000 6d2f0000 682f746e 00

21、706165 52455355 6f6f723d 4f480074 2f3d454d 52455400 74763d4d 00323031 48544150 73752f3d 69622f72 622f3a6e 2f3a6e69 2f727375 6e696273 62732f3a 2f3a6e69 2f746e6d 3a6e6962 746e6d2f 6962732f 6d2f3a6e 752f746e 732f7273 3a6e6962 7273752f 636f6c2f 732f6c61 3a6e6962 746e6d2f 7273752f 6e69622f 4853003a 3d4c4

22、c45 6e69622f 0068732f 3d445750 6d2f002f 682f746e 00706165 00000000Call Trace: Code: afc20018 8fc20018 24030041 8f848018 00000000 24843230 8fc50030 8f998110 Segmentation fault上面的藍(lán)色部分“epc = 004001dc, ra = 00400188”表示導(dǎo)致崩潰的指令的地址為0x004001dc,返回地址為0x00400188。不過(guò)返回地址我們不關(guān)注,因?yàn)樵诙褩P畔⒅幸部梢哉业健8鶕?jù)崩潰指令的地址值,可以判斷這條指令是在用

23、戶程序、內(nèi)核還是可加載模塊中:1. 用戶程序地址空間:0x000000000x7FFFFFFF;2. 內(nèi)核地址空間:System.map文件中的_stext _etext,大概是0x800000000x80300000;3. 可加載模塊地址空間:0xC00000000xC0800000由此可判斷,發(fā)生崩潰的指令屬于用戶程序。必須結(jié)合此程序的反匯編程序進(jìn)行回溯:將epc(0x004001dc)所在函數(shù)的部分反匯編代碼摘錄如下,以便分析:004001a0 : 4001a0:3c1c0fc0 luigp,0xfc0 4001a4:279c7fa0 addiugp,gp,32672 4001a8:03

24、99e021 addugp,gp,t9 4001ac:27bdffd0 addiusp,sp,-48 4001b0:afbc0010 swgp,16(sp) 4001b4:afbf0028 swra,40(sp) 4001b8:afbe0024 sws8,36(sp) 4001bc:afbc0020 swgp,32(sp) 4001c0:03a0f021 moves8,sp 4001c4:afc40030 swa0,48(s8) 4001c8:8fc20030 lwv0,48(s8) 4001cc:00000000 nop 4001d0:afc20018 swv0,24(s8) 4001d4:

25、8fc20018 lwv0,24(s8) 4001d8:24030041 liv1,65 4001dc:a0430000 sbv1,0(v0)/* 導(dǎo)致崩潰的指令 */ 4001e0:8f848018 lwa0,-32744(gp) 4001e4:00000000 nop 4001e8:24843230 addiua0,a0,12848 。需要明確一點(diǎn),上面棧信息中“Stack:”字樣開(kāi)始的內(nèi)容,即是函數(shù)C及它的更高幾級(jí)調(diào)用函數(shù)的棧內(nèi)容。下面對(duì)涉及的每個(gè)函數(shù)進(jìn)行分析:1. 函數(shù)C的棧:從函數(shù)C開(kāi)頭的指令“addiu sp,sp,-48”知道函數(shù)C的棧大小為48字節(jié)。即從“Stack:”字樣開(kāi)始

26、的48字節(jié):7fff7ebc 00000001 10008140 0040050c 10008140 00000000 00000003 0000000010008140 7fff7de8 00400188 00400170 返回地址函數(shù)C在開(kāi)始時(shí),會(huì)將用到的靜態(tài)寄存器、返回地址等,保存在堆棧中。我們關(guān)心的是返回地址??梢钥吹絩a的保存指令“4001b4:afbf0028 swra,40(sp)”,表示返回地址保存在堆棧的偏移地址40處,數(shù)值為0x00400188。根據(jù)這個(gè)地址值,在stack.dis中可以再次找到這個(gè)地址處于函數(shù)B的范圍內(nèi)。2. 函數(shù)B的棧:摘錄函數(shù)B開(kāi)頭幾條指令如下:004

27、00128 : 400128:3c1c0fc1 luigp,0xfc1 40012c:279c8018 addiugp,gp,-32744 400130:0399e021 addugp,gp,t9 400134:27bdffd8 addiusp,sp,-40 400138:afbc0010 swgp,16(sp) 40013c:afbf0020 swra,32(sp) 400140:afbe001c sws8,28(sp) 。由“400134:27bdffd8 addiusp,sp,-40”、“40013c:afbf0020 swra,32(sp)”可知函數(shù)B的棧大小為40字節(jié),函數(shù)B執(zhí)行完后

28、的返回地址存儲(chǔ)在其棧偏移地址32處。函數(shù)B棧的的數(shù)據(jù)緊挨著函數(shù)C的棧,取出羅列如下:00000003 00000002 0000000c 00000000 10008140 65642f00 10008140 7fff7e10 00400110 004000f8 返回地址從上面信息可以知道,函數(shù)B的返回地址為0x00400110,從stack.dis可知處于函數(shù)A的地址范圍內(nèi)。3. 函數(shù)A的棧:摘錄函數(shù)A開(kāi)頭幾條指令如下:004000b0 : 4000b0:3c1c0fc1 luigp,0xfc1 4000b4:279c8090 addiugp,gp,-32624 4000b8:0399e02

29、1 addugp,gp,t9 4000bc:27bdffd8 addiusp,sp,-40 4000c0:afbc0010 swgp,16(sp) 4000c4:afbf0020 swra,32(sp) 4000c8:afbe001c sws8,28(sp) 4000cc:afbc0018 swgp,24(sp) 。由“4000bc:27bdffd8 addiusp,sp,-40”、“4000c4:afbf0020 swra,32(sp)”可知函數(shù)A的棧大小為40字節(jié),函數(shù)A執(zhí)行完后的返回地址存儲(chǔ)在其棧偏移地址32處。函數(shù)A棧的的數(shù)據(jù)緊挨著函數(shù)B的棧,取出羅列如下:00000002 00000

30、001 7fff7ebc 00000005 10008140 00000000 10008140 7fff7e38 00400258 7fff7eb4 返回地址從上面信息可以知道,函數(shù)A的返回地址為0x00400258,從stack.dis可知處于函數(shù)main的地址范圍內(nèi)。至此,可以知道m(xù)ain調(diào)用A、A調(diào)用B、B再調(diào)用C時(shí),在函數(shù)C中導(dǎo)致程序崩潰?,F(xiàn)在認(rèn)真看一下函數(shù)C:21 void C(int c)22 23 char *p = (char *)c;24 *p = A;25 printf(%d: function Cn, c);26 這個(gè)函數(shù)太過(guò)簡(jiǎn)單,可以一眼就知道第23、24行代碼有問(wèn)題

31、。但是如果函數(shù)C有上千行代碼呢?除了睜大眼睛檢查C代碼外,我們還可以根據(jù)它的反匯編碼來(lái)差錯(cuò)內(nèi)核打印出來(lái)的棧信息的前面還有些有用的信息:do_page_fault() #2: sending SIGSEGV to heap for illegal write access to00000003 (epc = 004001dc, ra = 00400188)$0 : 00000000 10006c00 00000003 00000041 00000003 0000000c 0000000c 00000000$8 : 00006c00 00000002 00000000 42203a32 19999

32、999 00000000 00000057 00000115$16: 00000000 7fff7eb4 7fff7ebc 00000001 10005dc4 00000001 10005dbc 10005d94$24: 00000001 004001a0 10008140 7fff7db8 7fff7db8 00400188Hi : 00000002Lo : 00000000epc : 004001dc Not taintedStatus: 00006c13Cause : 3080000cProcess heap (pid: 70, stackpage=87876000)粉紅色部分是程序崩潰

33、時(shí)編號(hào)為031的所有寄存器的值,根據(jù)mips寄存器的使用約定,這些寄存器的編號(hào)、名字、功能如下表所示:寄存器編號(hào)助記符用法0zero返回值永遠(yuǎn)為01at用做匯編器的暫時(shí)變量2-3v0, v1 子函數(shù)調(diào)用返回結(jié)果4-7a0-a3 子函數(shù)調(diào)用的參數(shù)8-1524-25t0-t7t8-t9暫時(shí)變量,子函數(shù)使用時(shí)不需要保存與恢復(fù)16-23s0-s7 子函數(shù)寄存器變量。子函數(shù)必須保存和恢復(fù)使用過(guò)的變量在函數(shù)返回之前,從而調(diào)用函數(shù)知道這些寄存器的值沒(méi)有變化。26-27k0,k1 通常被中斷或異常處理程序使用作為保存一些系統(tǒng)參數(shù)28gp 全局指針。一些運(yùn)行系統(tǒng)維護(hù)這個(gè)指針來(lái)更方便的存取“static“和”ex

34、tern 變量。29sp 堆棧指針30s8/fp 第9個(gè)寄存器變量。子函數(shù)可以用來(lái)做楨指針31ra 子函數(shù)的返回地表1 mips寄存器使用規(guī)則現(xiàn)在回過(guò)頭來(lái)看看函數(shù)C的反匯編碼,從中找出出錯(cuò)的原因:004001a0 : 4001a0:3c1c0fc0 luigp,0xfc0 4001a4:279c7fa0 addiugp,gp,32672 4001a8:0399e021 addugp,gp,t9 4001ac:27bdffd0 addiusp,sp,-48 4001b0:afbc0010 swgp,16(sp) 4001b4:afbf0028 swra,40(sp) 4001b8:afbe002

35、4 sws8,36(sp) 4001bc:afbc0020 swgp,32(sp) 4001c0:03a0f021 moves8,sp 4001c4:afc40030 swa0,48(s8)/ a0為函數(shù)C的參數(shù) 4001c8:8fc20030 lwv0,48(s8)/ 現(xiàn)在v0=參數(shù) 4001cc:00000000 nop 4001d0:afc20018 swv0,24(s8) 4001d4:8fc20018 lwv0,24(s8) 4001d8:24030041 liv1,65/ v1=65=A 4001dc:a0430000 sbv1,0(v0)/ 相當(dāng)于*p=A 4001e0:8f84

36、8018 lwa0,-32744(gp) 。出錯(cuò)的指令為“4001dc:a0430000 sbv1,0(v0)”,它將寄存器v1的值存到地址(v0+0)中,只存儲(chǔ)1個(gè)字節(jié)。從上表可知:v1為3號(hào)寄存器,v0為2號(hào)寄存器,根據(jù)內(nèi)核打印出來(lái)的寄存器值可知v1=0x00000041,v0=0x00000003,寫地址為0x03,當(dāng)然出錯(cuò)這不是可寫的地址。閱讀匯編代碼是件困難的事情,沒(méi)有其他辦法時(shí)再用這方法吧。(3)、uClibc的堆實(shí)現(xiàn)原理當(dāng)使用malloc、calloc等動(dòng)態(tài)分配內(nèi)存函數(shù)時(shí),就要接觸到堆heap。了解了uClibc中堆的管理、實(shí)現(xiàn)機(jī)制,在解決由于數(shù)組越界、內(nèi)存泄漏等導(dǎo)致的奇怪問(wèn)題時(shí)

37、可以增加一個(gè)思路。一個(gè)程序需要更多的內(nèi)存時(shí),它可以向操作系統(tǒng)申請(qǐng),linux系統(tǒng)以4KB的整數(shù)倍(第一次可能例外)向用戶程序提供內(nèi)存,用戶程序?qū)⑦@部分內(nèi)存稱為“堆”,隨著申請(qǐng)內(nèi)存的增多,堆越來(lái)越大如圖1所示。uClibc封裝了向系統(tǒng)申請(qǐng)內(nèi)存、管理得到的內(nèi)存等操作,向用戶提供malloc、calloc、free、realloc等函數(shù)。uClibc堆管理的本質(zhì)在于:1. 使用malloc()或者calloc()可以動(dòng)態(tài)分配一段內(nèi)存,并向用戶返回一個(gè)內(nèi)存地址,而實(shí)際上這個(gè)地址前面有8個(gè)字節(jié)的內(nèi)部結(jié)構(gòu),用來(lái)記錄分配的塊長(zhǎng)度以及一些標(biāo)志。2. 使用free或者realloc釋放內(nèi)存時(shí),根據(jù)參數(shù)所示的地址

38、得到前面8字節(jié)的內(nèi)部結(jié)構(gòu),就可以知道不再使用的這段內(nèi)存的大小。這個(gè)結(jié)構(gòu)的完整部分如下:struct malloc_chunk size_t prev_size; /* Size of previous chunk (if free). */ size_t size; /* Size in bytes, including overhead. */ struct malloc_chunk* fd; /* double links used only if free. */ struct malloc_chunk* bk;prev_size是上一個(gè)塊的大小,只在上一個(gè)塊空閑的情況下才被填充。siz

39、e是當(dāng)前塊的大小,它包括prev_size和size成員的大小(8字節(jié)) ,它的最低位表示上一個(gè)塊是否空閑:1正在使用,0空閑。fd是雙向鏈表的向前指針,指向下一個(gè)塊。這個(gè)成員只在空閑塊中使用。bk是雙向鏈表的向后指針,指向上一個(gè)塊。這個(gè)成員只在空閑塊中使用。對(duì)于已分配的內(nèi)存,除了分配用戶指定大小的內(nèi)存空間外,還在前面增加了malloc_chunk結(jié)構(gòu)的前兩個(gè)成員(8字節(jié)).。下面以圖來(lái)說(shuō)明堆分配的過(guò)程:圖4 堆分配簡(jiǎn)圖注意:1. 上面的somesize是struct malloc_chunk的大小,即16字節(jié),這是為了編程方便要求的這不影響我們了解堆管理的本質(zhì)2. 圖中的“size | 0x

40、1”的最低位為1,表示前面一塊內(nèi)存已經(jīng)分配出去,正在使用free操作就是根據(jù)所傳遞的地址得到它前面8字節(jié)的內(nèi)部結(jié)構(gòu),從而知道這塊不再使用的內(nèi)存的大小,最后將它放入空閑隊(duì)列中。為了提高性能、減少碎片等,堆的實(shí)現(xiàn)比較復(fù)雜使用了大量的鏈表將不同大小范圍的塊鏈接在不同的隊(duì)列,但是這些都沒(méi)有背離上面說(shuō)的本質(zhì),僅僅是一些技巧性的操作。下面用一個(gè)圖來(lái)表示多次malloc、free后,uClibc中堆的管理結(jié)構(gòu),具體實(shí)現(xiàn)不再細(xì)說(shuō)。注意一點(diǎn),uClibc中僅僅維持著空閑的塊,對(duì)于已經(jīng)分配(malloc)出去的內(nèi)存,僅當(dāng)釋放(free)后,才會(huì)鏈入某個(gè)隊(duì)列中:圖5 某時(shí)刻堆中各空閑塊在鏈表中的分布(4)、內(nèi)存越界

41、、內(nèi)存泄漏了解了棧的作用、堆的實(shí)現(xiàn)后,現(xiàn)在來(lái)看看兩種情況的內(nèi)存越界:a) 局部變量數(shù)組越界程序3 strcpy.c01 #include 02 #include 03 #include 0405 int main(int argc, char *argv)06 07 char str2;08 if (argc 2)09 10 printf(Usage: %s n, argv0);11 12 else13 14 strcpy(str, argv1);15 printf(Input string: %sn, str);16 17 return 0;18 使用如下命令編譯得到可執(zhí)行文件strcpy,

42、反匯編文件strcpy.dis:mips-uclibc-gcc -o strcpy strcpy.c staticmips-uclibc-objdump -D strcpy strcpy.dis當(dāng)執(zhí)行./ strcpy abcdefghijklmno時(shí)一切正常,但是當(dāng)字符串增加1位時(shí)./ strcpy abcdefghijklmnop,程序崩潰。通過(guò)反匯編代碼我們可以知道此程序中main函數(shù)的棧使用情況:圖6 strcpy.c中main函數(shù)的棧執(zhí)行“./strcpy abcdefghijklmno”時(shí),“abcdefghijklmno”為15個(gè)字符,加上字符串結(jié)束符為16字節(jié),將會(huì)把上圖中從s

43、tr0到s8的區(qū)域完全覆蓋掉,但是返回地址ra仍保存完好。執(zhí)行“./ strcpy abcdefghijklmnop”時(shí),增加了一個(gè)字符,將會(huì)破壞棧中保存的ra ,這導(dǎo)致main函數(shù)執(zhí)行完后返回到錯(cuò)誤的地址??傊壕植孔兞吭浇鐚⑵茐臈#瑮V斜4娴氖巧弦粋€(gè)函數(shù)的執(zhí)行現(xiàn)場(chǎng)、和當(dāng)前函數(shù)的局部變量,所以造成的影響有可能在當(dāng)前函數(shù)中體現(xiàn),也可能在當(dāng)前函數(shù)執(zhí)行完后體現(xiàn)。b) malloc的內(nèi)存越界:使用malloc得到的內(nèi)存出現(xiàn)越界時(shí),導(dǎo)致的錯(cuò)誤更加隱蔽,例子如下:程序4 heap_crack.c01 #include 02 #include 03 #include 0405 int main(int a

44、rgc, char *argv)06 07 char *p1 = NULL;08 char *p2 = NULL;09 int size;1011 p1 = malloc(16);1213 printf(p1-4 = 0x%x, %dn, *(unsigned int *)&p1-4), *(unsigned int *)&p1-4);14 printf(p116 = 0x%x, %dn, *(unsigned int *)&p116), *(unsigned int *)&p116);15 printf(p120 = 0x%x, %dn, *(unsigned int *)&p120), *(unsigned int *)&p120);16 17 memset(p1, 0xff, 24);1819 if (argc = 2)20 21 size = strtoul(argv1, 0, 0);22 23 24 if (!size)25 26 size = 1024;27 28 29 printf(malloc second buffer,

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論