文章目录
指令的工作原理
注:指令和数据的地址是分开的;
内存并不是单独给某个程序使用的。对于多任务架构计算机,一个内存往往装载了多个程序的可执行二进制文件。程序装载的内存基地址也并不都从内存0地址开始。
指令装入的三种方式
绝对装入(不重要)
绝对装入只适用于单进程环境。在编译或汇编时(装载前),产生绝对地址的目标代码。程序中使用绝对地址。
可重定位装入(不重要)
动态运行时装入(重要)
动态重定位
可执行文件装入内存后,依然使用逻辑地址。需要一个重定位寄存器的支持。~这个重定位寄存器后来演进为段寄存器。
从写程序到程序运行
编译阶段:使用逻辑地址,每个目标模块地址都是从0开始。分为代码段、数据段。
链接阶段:统一修改了地址。代码段归档、数据段归档、BSS段归档。
加载后:根据链接后的地址+重定位寄存器=最终的物理地址。
代码段和数据段 有各自的重定位寄存器,即段寄存器。
段寄存器
段寄存器有ES、CS、SS、DS、FS、GS、LDTR、TR共8个。
ES:扩展段。在串操作时(比如cmovs)目标操作数的基址是ES,源操作数是DS。
CS:代码段,配合EIP使用。
SS: 堆栈段,凡是基址是EBP或ESP的,段前缀就是SS。
DS:数据段,默认的都是DS。
FS、GS:80386 之后定义的。
段寄存器结构:
只有段选择符对程序员是可见的。
qemu monitor中info registers可以看到段寄存器:
从左到右依次是:
sc->selector, sc->base, sc->limit, sc->flags & 0x00ffff00
ES =0000 0000000000000000 ffffffff 00c00000
CS =0010 0000000000000000 ffffffff 00a09b00 DPL=0 CS64 [-RA]
SS =0018 0000000000000000 ffffffff 00c09300 DPL=0 DS [-WA]
DS =0000 0000000000000000 ffffffff 00c00000
FS =0000 0000000000000000 ffffffff 00c00000
GS =0000 ffff8b053bc00000 ffffffff 00c00000
LDT=0000 0000000000000000 ffffffff 00c00000
TR =0040 fffffe12484d7000 00004087 00008b00 DPL=0 TSS64-busy
逻辑地址
在 Linux 操作系统中,用户态的虚拟地址通常指的是线性地址。在 Linux 中,逻辑地址通常指的是由段选择符和段内偏移量组成的地址,这种地址表示方式主要用于 x86 架构的机器。而线性地址是指一种平坦的统一地址空间,它不涉及任何段的概念。
线性地址
在用户态下,应用程序通过线性地址来访问虚拟内存。这些线性地址被映射到物理内存的地址,从而实现虚拟内存到物理内存的转换。在 Linux 中,这个映射过程由内核负责管理,并通过页表来实现。
逻辑地址通常对程序员是透明的,也就是说,程序员通常不需要直接处理逻辑地址。在高级编程语言中,程序员通常使用变量名来访问内存中的数据,编译器和操作系统负责将这些变量名转换为逻辑地址或线性地址。
然而,在一些低级编程语言或特定的操作系统环境中,逻辑地址可能会直接暴露给程序员。在这种情况下,程序员需要了解如何处理逻辑地址,以便正确地访问内存和执行操作。
总的来说,逻辑地址对于大多数程序员来说通常是不可见的,因为高级语言和操作系统抽象了底层硬件细节,使得程序员可以更加专注于程序设计和功能实现。
链接的三种方式
静态链接
装入时动态链接
运行时动态链接