Windows核心编程_静态编译和动态编译的区别
1.静态编译
静态就是将需要的系统dll打包进PE文件(关于PE文件的深刻介绍博主会在后面对其进行介绍)中,编译器会将这些模块码,或资源文件的数据,全部添加到可执行文件中,存放在可执行文件的模块区或资源区,并且做文件地址映射!
这样在运行的时候Windows就会将这些数据映射到内存中(文件与内存偏移映射后面会介绍)
然后需要用到这些dll或者资源时,直接jmp或call或mov去引用这些数据段上的数据即可!
并且引用时的数据大小编译器在编译期间通过文件地址的映射就已经决定了!
比如我们引用一个图像,使用的是静态编译,那么这个数据就会被加载到文件的任何位置并以段来做划分!
然后在引用代码:
image *img = MY_IMAG(资源宏)
编译器会先将资源加载进来,然后替换成
image *img = 0x2b3c4d(映射后的地址)
因为有的时候我们安装了Visual Studio的运行时库,这些库的dll都存放到了系统目录下,但是有的操作系统却没有安装这些运行时库,导致在其它系统上运行总是报缺少dll,而且下载的时候版本不对,函数入口点以及参数可能都不对,也就是向前兼容不好!
所以静态编译是非常好的一个选择!
设置方法:
2.动态编译
动态编译即将需要打包的动态库写入到PE头文件结构里,让Windows的PE加载器自己去解析和加载!
和上面一样映射
映射地址也是在编译期间编译器会确定
比如我们load了一个dll,并引用了其中的一个函数:addtest
那么如下:
load("TEST.dll");
addtest();
在编译期间,编译器会打开dll文件,在里面寻找addtest函数的入口偏移地址,然后加载到内存后产生映射段地址关系,直接可以根据这个偏移量来直接加载即可!
其实整个编译过程很复杂,因为在此过程中可能会加载其它dll,所以编译器在编译期间会生成一个结构表,来保存每个dll模块名,每当我们调用一个loaddll之类的函数,编译器就会先计算这个dll会被加载到哪儿,注意模块是优先包含进来的,也就是在编译我们的代码之前,编译器会先将dll编译到文件里,然后在编译我们的,不然这样调用顺序就有问题了,我们还没加载dll,就调用dll了!
然后将这些计算出来的dll入口地址保存到这个表里
然后我们在调用addtest的时候,编译器会在dll文件里将这个函数的入口地址给找出来,然后调用的时候地址=编译期间结构表里保存的dll地址+入口地址=完整的api地址!
这就是虚拟映射内存的魅力所在,解决了变址的问题!
步骤和上面一样只是用多线程DLL