根据我对编译程序(例如C程序)的理解,编译器将获取您的代码并以二进制(即目标拱的机器代码)格式输出可执行程序。
在此二进制文件中,您将具有指向内存中地址的指令,以从程序其他部分加载数据/指令。
鉴于此程序将在任意位置加载到内存中,该程序如何知道这些内存地址是什么?他们是如何设置/计算的,这是谁的工作?
[[例如,二进制文件是否仅具有占位符,表示操作系统首次将其加载到内存中时所取代的内存位置?
如果它需要动态加载共享库,它将如何计算出该存储库的位置?
“虚拟记忆”如何发挥作用? (如果有的话)
程序(及其作者)不知道what内存地址在加载到计算机内存中时的地址,它仅相对于段的开头知道
where占位符。这就是为什么编译器为每个此类占位符随附relocation record。重定位是一条信息,告诉操作系统或链接器
重定位地址在哪里(它在代码或数据段中的偏移量)它在哪个段中
- 它指的是哪个段或符号
- 该地址应采用哪种搬迁方式
请考虑以下Windows Portable可执行程序的简单代码或源代码: [.text] Main:NOP LEA ESI,[Mem] ; more instructions [.data] DB "Some data" Mem: DB "Other data"
将转换为机器指令和存储器数据:
|[.text] |[.text] |00000000:90 |Main:NOP |00000001:8D35[09000000] | LEA ESI,[Mem] |00000007: | ; more instructions |[.data] |[.data] |00000000:536F6D6520646174~| DB "Some data" |00000009:4F74686572206461~|Mem: DB "Other data"
编译器不知道
Mem
的虚拟地址,它只知道它位于0x00000009
段的开头的.data
个字节,因此它将这个临时编号放入LEA ESI,[Mem]
的操作代码中并创建相对于段.text
的占位符的重定位(位于段0x00000003
中的偏移.data
处)。在链接时,链接程序决定将.text
段加载到虚拟地址0x00401000
,将其加载到VA.data
的0x00402000
段。然后,链接器读取重定位记录并通过添加0x00402000
来修改占位符。然后,链接的可执行文件中的指令LEA ESI,[Mem]
将为8D3509204000
,这是Mem
的最终固定虚拟地址。我们将能够在运行时在调试器中看到该地址。在链接的可执行文件(16位DOS MZ或Windows PE)中也存在重定位,以防无法在链接时假定的虚拟映像库地址处加载重定位。在Linux中链接SO库更加复杂,请参见http://www.skyfree.org/linux/references/ELF_Format.pdf中的
2动态链接章