链接地址是执行程序的地址,而加载地址是实际放置程序的内存地址。
现在我很困惑程序计数器的值是多少?是加载地址还是链接地址?
链接地址是执行程序的地址,而加载地址是实际放置程序的内存地址。
现在我很困惑程序计数器的值是多少?是加载地址还是链接地址?
链接地址是执行程序的地址
不,这不对。
而加载地址是内存中实际放置程序的地址。
有点儿。程序通常由多条指令组成,因此不能将其放置在单个“加载地址”。
当人们谈论加载地址时,他们通常谈论可以重定位(在运行时)到任意加载地址的可重定位代码。
例如,让我们看一个链接在地址的程序,它0x20020
由 100 条 4 字节指令组成,所有指令都按顺序执行(例如,它是一个ADD
s 序列,后跟一个SYSCALL
退出程序)。
如果这样的程序在地址处加载0x20020
,则在运行时程序计数器将具有值0x20020
,然后它将前进到下一条指令 at 0x20024
,然后到0x20028
,依此类推,直到到达程序的最后一条指令 at 0x201ac
。
但是如果该程序在地址加载0x80020020
(即如果程序0x80000000
从其链接地址重定位),则程序计数器将从 开始0x80020020
,最后一条指令将在0x800201ac
。
请注意,在许多操作系统上,可执行文件是不可重定位的,因此必须始终在它们链接的同一地址加载(即使用重定位0
;在这种情况下,“链接地址”实际上是执行开始的地址),而共享库几乎总是可重定位的,并且通常在地址处链接0
并且具有非零重定位。
两者是不同的概念,在不同的上下文中使用。Linker/Loader主要负责代码重定位和修改;PC 是一个数字计数器,它指示程序序列的定位(不是类型的地址/位置,如链接器/加载器)。
链接和加载:-
链接器或加载器操作的核心是重定位和代码修改。当编译器或汇编器生成目标文件时,它使用文件中定义的代码和数据的未重定位地址生成代码,并且对于其他地方定义的代码和数据通常为零。作为链接过程的一部分,链接器修改目标代码以反映分配的实际地址。例如,考虑使用 eax 寄存器将变量 a 的内容移动到变量 b 的 x86 代码片段。
mov a,%eax mov %eax,b
如果 a 在同一个文件中的 1234 hex 位置定义并且 b 从其他地方导入,则生成的目标代码将是:
A1 34 12 00 00 mov a,%eax A3 00 00 00 00 mov %eax,b
每条指令包含一个一字节的操作码,后跟一个四字节的地址。第一条指令引用了 1234(字节反转,因为 x86 使用从右到左的字节顺序),第二条指令引用了 0,因为 b 的位置未知。
现在假设链接器链接此代码,使得 a 所在的部分被重定位十六进制 10000 字节,而 b 原来位于十六进制 9A12。链接器将代码修改为:
A1 34 12 01 00 mov a,%eax A3 12 9A 00 00 mov %eax,b
也就是说,它在第一条指令中的地址上加了 10000,所以现在它引用了 a 的重定位地址,即 11234,并修补了 b 的地址。这些调整会影响指令,但目标文件数据部分中的任何指针也必须调整。
程序计数器:-
程序计数器 (PC) 是一个处理器寄存器,用于指示计算机在其程序序列中的位置。
在典型的中央处理单元 (CPU) 中,PC 是一个数字计数器(这是术语“程序计数器”的起源),它可能是 CPU 硬件中的许多寄存器之一。指令周期以取指开始,其中 CPU 将 PC 的值放在地址总线上以将其发送到内存。
存储器通过在数据总线上发送该存储器位置的内容来响应。(这是存储程序计算机模型,其中可执行指令与普通数据一起存储在内存中,并由它进行相同的处理)。
在获取之后,CPU 继续执行,根据它获得的内存内容采取一些行动。在这个周期的某个时刻,PC 将被修改,以便执行的下一条指令是不同的(通常,递增,以便下一条指令是从紧跟当前指令的最后一个内存位置的内存地址开始的指令) .
我会把“加载地址”这个词放在你的想法之外。它在现代操作系统中并不真正存在。在过去,多个程序加载到同一个地址空间(并且每个程序加载到一个连续的内存区域)中,加载地址具有重要意义。现在它没有。他就是为什么。
一个可执行文件通常会定义许多不同的程序段。这些可能不会连续加载到内存中。例如,链接器经常指导远离程序其他区域的堆栈区域的创建。
可执行文件将指示应该是 PC 初始值的位置。这可能不在程序段的开头,更不用说在第一个程序段中了。