在
理解程序內(nèi)存一文中我們介紹了普通程序運(yùn)行時(shí)在內(nèi)存中的布局,下面我們專門針對(duì)C++源代碼以WinDbg為工具分析下C++程序的變量存儲(chǔ)模型, 要理解下面的知識(shí),請(qǐng)先看懂
理解程序內(nèi)存一文。
下面我們嘗試分析C++變量的存儲(chǔ)模型, 我們的測(cè)試程序非常簡(jiǎn)單:
#include <iostream>using namespace std;const char* global_const_string = "hello world";
int global_int = 20;
static int global_static_int = 30;
int main()
{
static int local_static_int = 100;
int local_int = 200;
int* pValue = new int(300);
cout << global_const_string << global_int
<< global_static_int << local_static_int
<< local_int << *pValue;
delete pValue;
system("pause");
return 0;
}
可以看到我們上面對(duì)程序雖然簡(jiǎn)單,但是基本包括了所有的變量類型,包括靜態(tài)的,常量的,全局的,本地的,還有new出來的,下面我們依次分析每個(gè)變量所屬的存儲(chǔ)區(qū)域。
我們直接用WinDbg以源碼的方式調(diào)試我們的測(cè)試程序consoleTest.exe.
首先我們分析下consoleTest.exe模塊的起始地址及內(nèi)部數(shù)據(jù)節(jié)的分布情況, 通過!address命令:
* 400000 401000 1000 MEM_IMAGE MEM_COMMIT PAGE_READONLY Image "ConsoleTest.exe"
|- 401000 41d000 1c000 MEM_IMAGE MEM_COMMIT PAGE_EXECUTE_READ Image "ConsoleTest.exe"
|- 41d000 422000 5000 MEM_IMAGE MEM_COMMIT PAGE_READONLY Image "ConsoleTest.exe"
|- 422000 426000 4000 MEM_IMAGE MEM_COMMIT PAGE_WRITECOPY Image "ConsoleTest.exe"
|- 426000 427000 1000 MEM_IMAGE MEM_COMMIT PAGE_READONLY Image "ConsoleTest.exe"
可以看到consoleTest.exe模塊在內(nèi)存中的起始地址是0x400000, 接下來可以通過!dh 0x400000分析它內(nèi)部的數(shù)據(jù)節(jié)分布, 并且最終我們可以得出如下結(jié)論:
地址 400000 - 401000 : PE文件頭,屬性是只讀
地址 401000 - 41d000 : .text, 屬性是只讀可執(zhí)行,表示代碼節(jié)
地址 41d000 - 422000 : .rdata, 屬性是只讀, 表示只讀數(shù)據(jù)
地址 422000 - 426000 : .data, 屬性是寫入時(shí)拷貝,表示可讀寫數(shù)據(jù)
地址 426000 - 427000 : .rsrc, 屬性是只讀,表示資源節(jié)
通過!address -f:stack命令我們可以看到:
0:000> !address -f:stack
BaseAddr EndAddr+1 RgnSize Type State Protect Usage
-------------------------------------------------------------------------------------------
40000 13d000 fd000 MEM_PRIVATE MEM_RESERVE Stack [8b0.1d0; ~0]
13d000 13e000 1000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE|PAGE_GUARD Stack [8b0.1d0; ~0]
13e000 140000 2000 MEM_PRIVATE MEM_COMMIT PAGE_READWRITE Stack [8b0.1d0; ~0]
可以看到我們主線程的堆棧起始地址是: 13e000 - 140000
接下來我們首先分析所有全局變量的存儲(chǔ)區(qū)域, 通過x consoletest!global*命令,讓調(diào)試器列出所有在consoletest模塊中g(shù)lobal開頭的調(diào)試符號(hào):
0:000> x consoletest!global*
00422000 ConsoleTest!global_const_string = 0x0041d1dc "hello world"
00422004 ConsoleTest!global_int = 0n20
00422008 ConsoleTest!global_static_int = 0n30
004238a0 ConsoleTest!global_locale = 0x00000000
通過分析我們可以看到我們的3個(gè)全局變量global_const_string, global_int, global_static_int全都分布在422000 - 426000之間的.data可讀寫數(shù)據(jù)節(jié)中。
而global_const_string所指向的內(nèi)容
則分布在41d000 - 422000 之間的.rdata只讀數(shù)據(jù)節(jié)中,這個(gè)結(jié)論也符合我們平時(shí)關(guān)于全局變量存儲(chǔ)區(qū)域的理解。
下面我們?cè)賴L試分析局部變量的存儲(chǔ)區(qū)域,再main函數(shù)內(nèi)部cout的地方設(shè)置斷點(diǎn),然后讓程序運(yùn)行到此, 然后輸入dv /t /i /v命令查看所有局部變量, 可以看到
0:000> dv /t /i /v
prv local 0042200c int local_static_int = 0n100
prv local 0013ff70 int local_int = 0n200
prv local 0013ff74 int * pValue = 0x02248ff8
我們可以看到local_static_int也分布在422000 - 426000之間的.data可讀寫數(shù)據(jù)節(jié)中, 而local_int和pValue則都存儲(chǔ)在
13e000 - 140000之間的堆棧區(qū)域上。
而指針pValue所指向的地址0x02248ff8我們可以通過!address 0x02248ff8命令來分析, 結(jié)果是:
0:000> !address 0x02248ff8
Usage: Heap
Allocation Base: 021d0000
Base Address: 02248000
End Address: 02249000
Region Size: 00001000
Type: 00020000 MEM_PRIVATE
State: 00001000 MEM_COMMIT
Protect: 00000004 PAGE_READWRITE
More info: !heap -p 0x21d1000
More info: !heap -p -a 0x2248ff8
可以看到地址
0x02248ff8是在堆(heap)上面。
通過上面的分析,我們驗(yàn)證了平時(shí)C++書上關(guān)于各種類型變量存儲(chǔ)區(qū)域的假設(shè),簡(jiǎn)單來說就是全局變量和靜態(tài)變量會(huì)被編譯到可執(zhí)行文件的數(shù)據(jù)節(jié)(分只讀和可讀寫)中, 非靜態(tài)的局部變量則分配在堆棧(stack)上,而new(malloc)出來的內(nèi)存則分配在堆(heap)上。
posted on 2012-09-20 21:57
Richard Wei 閱讀(2470)
評(píng)論(0) 編輯 收藏 引用 所屬分類:
C++