zoukankan      html  css  js  c++  java
  • 利用Windbg深入理解变量的存储模型

    下面的是一个简单的测试程序,基本包括了所有的变量类型,包括静态的,常量的,全局的,本地的,还有new出来的

    #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;
    }

    下面我们依次分析每个变量所属的存储区域:

    我们直接用WinDbg以源码的方式调试我们的测试程序consoleTest.exe.
    首先我们分析下consoleTest.exe模块的起始地址及内部数据节的分布情况, 通过!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模块在内存中的起始地址是0x400000, 接下来可以通过!dh 0x400000分析它内部的数据节分布, 并且最终我们可以得出如下结论:
    地址 400000 - 401000 : PE文件头,属性是只读
    地址 401000 - 41d000 : .text, 属性是只读可执行,表示代码节
    地址 41d000 -  422000 : .rdata, 属性是只读, 表示只读数据
    地址 422000 -  426000 : .data, 属性是写入时拷贝,表示可读写数据
    地址 426000 - 427000 : .rsrc, 属性是只读,表示资源节

    通过!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

    接下来我们首先分析所有全局变量的存储区域, 通过x consoletest!global*命令,让调试器列出所有在consoletest模块中global开头的调试符号:
    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个全局变量global_const_string, global_int, global_static_int全都分布在422000 - 426000之间的.data可读写数据节中。
    而global_const_string所指向的内容
    0x0041d1dc "hello world"
    则分布在41d000 -  422000 之间的.rdata只读数据节中,这个结论也符合我们平时关于全局变量存储区域的理解。

    下面我们再尝试分析局部变量的存储区域,再main函数内部cout的地方设置断点,然后让程序运行到此, 然后输入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可读写数据节中, 而local_int和pValue则都存储在13e000 - 140000之间的堆栈区域上。

    而指针pValue所指向的地址0x02248ff8我们可以通过!address 0x02248ff8命令来分析, 结果是:
    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)上面。

    通过上面的分析,我们验证了平时C++书上关于各种类型变量存储区域的假设,简单来说就是全局变量和静态变量会被编译到可执行文件的数据节(分只读和可读写)中, 非静态的局部变量则分配在堆栈(stack)上,而new(malloc)出来的内存则分配在堆(heap)上。

       转自 http://www.cppblog.com/weiym/archive/2012/09/20/191429.html

  • 相关阅读:
    MS SQL入门基础:使用SQL Server Profiler
    收藏夹路径设置
    MS SQL入门基础:数据转换服务基本概念
    MS SQL入门基础:复制的概述和术语
    MS SQL入门基础:创建备份设备
    巧用ASP生成PDF文件
    MS SQL入门基础:sql 警报
    MS SQL入门基础:基于HTTP的数据访问
    MS SQL入门基础:XML文档与数据库表
    MS SQL入门基础:复制监视器
  • 原文地址:https://www.cnblogs.com/Gotogoo/p/5381838.html
Copyright © 2011-2022 走看看