Visual C++从编译到执行Part I: 程序启动

本文主要探讨 Visual C++ 程序如何从编译到执行,在这个过程中,VC 生成的程序如何与操作系统交互,初始化,运行以及结束.同时还讨论了一些Visual C++ Runtime Library 的内在机制,包括内存管理等等.

要知道一个程序怎么从编译到执行,首先得了解一下 PE 文件格式,也就是我们常用的 Windows Executable 的格式。(事实上这种格式并不只限于 Windows Exectuable)。关于这个 topic 最好的文章是 Matt Pietrek写的An In-Depth Look into the Win32 Portable Executable File Format,网上 google 一下即得。简单来讲,PE格式的文件 Header里包含下面的结构:

IMAGE_DOS_HEADER
IMAGE_NT_HEADERS

如果你用Hex editor打开一个executable文件,最前面是IMAGE_DOS_HEADER,这个一般不太重要.紧跟其后的就是IMAGE_NT_HEADERS.

这些结构定义在 <winnt.h>. IMAGE_NT_HEADERS定义如下 (以32bit为例):

typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

其中,IMAGE_OPTIONAL_HEADER32定义如下:

typedef struct _IMAGE_OPTIONAL_HEADER {
    WORD Magic;
    BYTE MajorLinkerVersion;
    BYTE MinorLinkerVersion;
    DWORD SizeOfCode;
    DWORD SizeOfInitializedData;
    DWORD SizeOfUninitializedData;
    DWORD AddressOfEntryPoint; //Entry Point
    DWORD BaseOfCode;
    DWORD BaseOfData;

    DWORD ImageBase;
    DWORD SectionAlignment;
    DWORD FileAlignment;
    ...
    DWORD SizeOfImage;
    DWORD SizeOfHeaders;
    DWORD CheckSum;
    WORD Subsystem;
    ...
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

IMAGE_OPTIONAL_HEADER32中, AddressOfEntryPoint定义了程序的入口点, 即当Windows系统完成Loading之后, 从哪个Virtual Address开始把控制转给executable本身. 不过这个入口函数既不是Windows启动一个Process时开始执行的第一个函数, 也不是C++程序里面的main函数. 关于Windows Kernel 如何完成Process loading的, 在Mark E. Russinovich, David A. Solomon的Microsoft Windows Internals, Fourth Edition: Microsoft Windows Server(TM) 2003, Windows XP, and Windows 2000 里面讲得比较清楚. 我们就考虑系统把控制转到EntryPoint之后吧.

Visual C++的编译器的executable的入口函数是mainCRTStartup, 或wmainCRTStartup if compiled as UNICODE, 以上是编译Console Executable.如果是Windows程序, 则是 (w)WinMainCRTStartup. 函数定义在 crt0.c 里可以找到. 通常在main函数里设个断点, 停下来后在 Callstack里就可以点到mainCRTStartup里去看看. 你也可以定义自己的入口函数, 然后用Linker选项: /ENTRYR指定自己的入口函数. 但是因为入口函数干了很多初始化 C++ Library之类的工作, 所以如果你自己的入口函数没有做类似的初始化的话, 多半程序没法运行. Linker 编译的时候, 确定了入口函数的地址, 就把这个地址写入PE Header中.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值