程序在内存中的外观如何?

sha*_*kin 9 memory winapi program-structure

程序(例如C或C++)如何安排在计算机内存中?我对段,变量等有点了解,但基本上我对整个结构没有扎实的了解.

由于内存中的结构可能不同,我们假设Windows上有一个C++控制台应用程序.

一些指向我具体的指示:

  • 功能概要,如何调用?
  • 每个函数都有一个堆栈框架,它包含什么以及它如何安排在内存中?
  • 函数参数和返回值
  • 全局变量和局部变量?
  • const静态变量?
  • 线程本地存储..

类似教程的材料的链接是受欢迎的,但请不要参考样式材料,假设汇编程序等知识.

Seb*_*ian 10

这可能是你要找的:

http://en.wikipedia.org/wiki/Portable_Executable

PE文件格式是Windows二进制文件(.exe,.dll等)的二进制文件结构.基本上,它们被映射到内存中.这里描述了更多细节,并解释了如何自己查看内存中加载的dll的二进制表示:

http://msdn.microsoft.com/en-us/magazine/cc301805.aspx

编辑:

现在我知道您想了解源代码如何与PE文件中的二进制代码相关.这是一个巨大的领域.

首先,您必须了解计算机体系结构的基础知识,这将涉及学习汇编代码的一般基础知识.任何"计算机体系结构简介"大学课程都可以.文献包括例如"John L.Hennessy和David A.Patterson.计算机体系结构:定量方法"或"Andrew Tanenbaum,结构化计算机组织".

阅读本文后,您应该了解堆栈是什么以及它与堆的区别.堆栈指针和基指针是什么,返回地址是什么,有多少寄存器等.

一旦你理解了这一点,将各个部分拼凑起来相对容易:

C++对象包含代码和数据,即成员变量.一类

class SimpleClass {
     int m_nInteger;
     double m_fDouble;

     double SomeFunction() { return m_nInteger + m_fDouble; }
}
Run Code Online (Sandbox Code Playgroud)

将是内存中4 + 8个连续字节.当你这样做时会发生什么:

SimpleClass c1;
c1.m_nInteger = 1;
c1.m_fDouble = 5.0;
c1.SomeFunction();
Run Code Online (Sandbox Code Playgroud)

首先,在堆栈上创建对象c1,即堆栈指针esp减少12个字节以腾出空间.然后将常数"1"写入存储器地址esp-12,并将常数"5.0"写入esp-8.

然后我们调用一个意味着两件事的函数.

  1. 计算机必须将二进制PE文件的一部分加载到包含函数SomeFunction()的内存中.无论您创建多少个SimpleClass实例,SomeFunction都只会在内存中一次.

  2. 计算机必须执行SomeFunction()函数.这意味着几件事:

    1. 调用该函数还意味着传递所有参数,通常这是在堆栈上完成的.SomeFunction有一个(!)参数,这个指针,即指向堆栈上的内存地址的指针,我们刚刚写了值"1"和"5.0"
    2. 保存当前程序状态,即当前指令地址,即SomeFunction返回时将执行的代码地址.调用函数意味着按下堆栈上的返回地址并将指令指针(寄存器eip)设置为函数SomeFunction的地址.
    3. 在函数SomeFunction中,通过将旧的基指针(ebp)存储在堆栈上(push ebp)并使堆栈指针成为新的基指针(mov ebp,esp)来保存旧堆栈.
    4. 执行SomeFunction的实际二进制代码,它将调用将m_nInteger转换为double的机器指令并将其添加到m_fDouble.m_nInteger和m_fDouble位于堆栈上,位于ebp-x字节.
    5. 添加的结果存储在寄存器中,函数返回.这意味着堆栈被丢弃,这意味着堆栈指针被设置回基本指针.基指针被设置回(堆栈上的下一个值),然后指令指针被设置为返回地址(堆栈上的下一个值).现在我们又回到原始状态,但在某些寄存器中潜伏着SomeFunction()的结果.

我建议,你自己构建这样一个简单的例子并逐步完成反汇编.在调试版本中,代码将易于理解,Visual Studio在反汇编视图中显示变量名称.查看寄存器esp,ebp和eip的作用,内存在哪里分配对象,代码在哪里等等.