查了很多资料,网上能参考的真实太少了。
《程序员的自本人修养》里面明确了Linux下跨跨模块调用函数或访问数据,使用GOT实现地址无关。
具体的就是调用某一个函数前,找到GOT中对应项,然后跳转到对应项中保存的目标地址,该目标地址是在动态链接器链接时
填充的,GOT放在数据段,故能够做到地址无关,多个程序共享同一份代码指令。
但是DLL为什么不行呢?
《程序员的自本人修养》里面明确了Linux下跨跨模块调用函数或访问数据,使用GOT实现地址无关。
具体的就是调用某一个函数前,找到GOT中对应项,然后跳转到对应项中保存的目标地址,该目标地址是在动态链接器链接时
填充的,GOT放在数据段,故能够做到地址无关,多个程序共享同一份代码指令。
但是DLL为什么不行呢?
书上看到,在调用导入函数的时候,使用的指令中的目标地址是常量,即是函数在导入地址表中的项,然后间接跳转。
但是本人比较笨,看不出来为什么DLL要将目标地址写成常量呢?不能在静态库链接时将符号写入地址表,然后动态链接时改写对应的导入符号的目标地址,这样本人调用符号的时候,不是可以直接在导入地址表中找到函数对应的项,然后间接跳转吗?
想不通。而且这样的话,岂不是做不到DLL共享?那么windows下DLL岂不是少了 节省内存 这个大优势吗?
解决方案
10
Dll里保存各个导出函数的地址都是相对偏移地址,其在内存中的最终地址由加载器加载到内存,并映射到可执行文件的指定的虚拟内存空间中的地址确定。
例如dll加载到内存并映射到exe程序的虚拟地址0x800000,那么dll的某导出函数的偏移为0x1000,那么执行导出函数的虚拟地址就是0x801000
例如dll加载到内存并映射到exe程序的虚拟地址0x800000,那么dll的某导出函数的偏移为0x1000,那么执行导出函数的虚拟地址就是0x801000
5
VMMap 是进程虚拟和物理内存分析实用工具。http://technet.microsoft.com/zh-cn/sysinternals/dd535533
10
DLL导入表中地址是相对地址,DLL实际在不同程序中的加载的地址可能不同的,原因是地址可能已经被用,DLL中会有一个建议的地址,假如不能加载到这个地址,系统会重新选一个合适的地址,然后修改重定位表中的数据
10
dll 早期Windows dll ,用int 20h 指令实现跳转到函数的
int 20h 指令后面是一个索引之类的东西。
当,函数第一次调用的实话哦,int 20指令,根据指令后面的数字,找到对应的函数地址
然后,改写int 20h指令 为函数真实地址,
以后调用此函数(第一次要查找函数内存地址),直接跳转到 加载后的内存地址。
不知道现在还用不用这么做,还是DLL一旦加载,就定位DLL地址。
int 20h 指令后面是一个索引之类的东西。
当,函数第一次调用的实话哦,int 20指令,根据指令后面的数字,找到对应的函数地址
然后,改写int 20h指令 为函数真实地址,
以后调用此函数(第一次要查找函数内存地址),直接跳转到 加载后的内存地址。
不知道现在还用不用这么做,还是DLL一旦加载,就定位DLL地址。
5
实际上,Windows的DLL,相当一部分,至少Windows 3大模块,
内存地址是固定的,不需要重定位了,每个进程中,这些模块,都是使用的同一个内存地址
Windows DLL 32Bits,16BIts,在VC的编译选项中
有一个 入口地址设定
在不和其他模块产生地址冲突的情况下,
全部函数都不需要重定位,直接call 或jump 到导出表指定的内存,就可以执行了
假如有冲突,则需要重定位
内存地址是固定的,不需要重定位了,每个进程中,这些模块,都是使用的同一个内存地址
Windows DLL 32Bits,16BIts,在VC的编译选项中
有一个 入口地址设定
在不和其他模块产生地址冲突的情况下,
全部函数都不需要重定位,直接call 或jump 到导出表指定的内存,就可以执行了
假如有冲突,则需要重定位
10
无论怎么样,跳转(调用)指令都是需要的
跳转(调用)指令需要一个具体的地址,而不是编号。
所以所谓地址无关,其实不过是通过一种间接方式,跳转到对应函数位置(函数地址总是需要的)。
实际上,windows 有两种表
一种是字符串类型的的函数名 索引的表格
一种是函数索引号,索引的表格
函数可以通过名导出,也可以通过索引号(一个整数)导出
这其实就可以算作不通过地址调用了
至于重定位,不过是代码的需要
代码既然加载在不同内存地址(虚拟地址),那就要保证能够调用到他,
就要保证程序中,跟地址定位有关的代码。移动到不同地址,不影响指令的运行。
重定位表本身,不是给动态库用的
全部代码中,跟(绝对)定位相关的指令,加载在不同地址,都有可能要修改一下
这就是所谓的重定位。
包括变量的地址,都可需要重定位。
要重定位的是指令,是跟指令相关的符号
不是仅仅是函数。
跳转(调用)指令需要一个具体的地址,而不是编号。
所以所谓地址无关,其实不过是通过一种间接方式,跳转到对应函数位置(函数地址总是需要的)。
实际上,windows 有两种表
一种是字符串类型的的函数名 索引的表格
一种是函数索引号,索引的表格
函数可以通过名导出,也可以通过索引号(一个整数)导出
这其实就可以算作不通过地址调用了
至于重定位,不过是代码的需要
代码既然加载在不同内存地址(虚拟地址),那就要保证能够调用到他,
就要保证程序中,跟地址定位有关的代码。移动到不同地址,不影响指令的运行。
重定位表本身,不是给动态库用的
全部代码中,跟(绝对)定位相关的指令,加载在不同地址,都有可能要修改一下
这就是所谓的重定位。
包括变量的地址,都可需要重定位。
要重定位的是指令,是跟指令相关的符号
不是仅仅是函数。
5
MS Windows技术,
相对Linux 比较陈旧吧
改变缓慢
虽然MS对变革也很敏感,
但一般是收买或吸收,
MS 内部也许有很多创新,有很多创举
但是应用的时候,需要决策,哪些是目前可以推出的,以及要不要兼容旧的特征
不可能比较实时,推出新技术。
。MS VC 对标准的支持,可见一斑。
其实MS推出 64Bit ABI 就比较果断
ms 推出.net,C# 也比较果断
而MS, Vista,Win7,Win8,Win10 推出的也比较迅猛
Linux 不是商业项目,所以更容易接收,自主创新,
毕竟代码,都是各位大神本人编写的。
而MS主要是公司行为,决策是以公司利益为主的。
—
PS :本人不是高手,水平有限,高手们其实都没有发言吧
相对Linux 比较陈旧吧
改变缓慢
虽然MS对变革也很敏感,
但一般是收买或吸收,
MS 内部也许有很多创新,有很多创举
但是应用的时候,需要决策,哪些是目前可以推出的,以及要不要兼容旧的特征
不可能比较实时,推出新技术。
。MS VC 对标准的支持,可见一斑。
其实MS推出 64Bit ABI 就比较果断
ms 推出.net,C# 也比较果断
而MS, Vista,Win7,Win8,Win10 推出的也比较迅猛
Linux 不是商业项目,所以更容易接收,自主创新,
毕竟代码,都是各位大神本人编写的。
而MS主要是公司行为,决策是以公司利益为主的。
—
PS :本人不是高手,水平有限,高手们其实都没有发言吧