数组的偏移量是如何表示数组的起始地址的

How did offset of an array come to mean the starting address of an array

考虑汇编代码 - mov edi, offset newarray
根据我的阅读,这会将 newarray 的地址放入寄存器 edi
我不明白的是这个词offset这个词的英文意思是什么
适合这里。

x86 内存地址的形式为 segment:offset,其中偏移量部分位于普通(通用)寄存器中,如 EDI(或 64 位代码中的 RDI)。

在我们使用扁平内存模型的现代系统中,segment 基数始终为 0,而 offset 整个地址,等于到线性地址。

x86-64 寻址 mov eax, [rdi + rax*4 + 1234]:

  • RDI是中的基址寄存器.

    • RAX是索引(因为它有比例因子,或者说[rdi+rax]中的第2个寄存器)
    • 1234 是位移,编码为双字 (disp32) 符号扩展为 64 位
  • RDI + RAX*4 + 1234就是那个寻址方式计算出来的有效地址。 (因此 lea eax, fs:[rdi + rax*4 + 1234] 给你那个,即使 FS 段基数不为零。)这是 seg:off 地址的 offset 部分。

  • RDI 作为基址寄存器意味着 DS 段。 seg:off -> 线性地址DS_base + offset。 (等于偏移量是因为CS、DS、ES、SS基数在64位模式下固定为0,32位模式下主流OSes设置为0。)

  • 线性地址是一个虚拟地址因为64位模式需要启用分页

  • virt->phys 转换是通过在由 TLB 缓存的页表中查找页码部分(底部 12 以上的位)来实现的。 ()。如果物理地址是设备地址,则生成的物理地址用于访问内存(通过缓存)或 MMIO over PCIe。

所以是的,要索引数组,您需要 MASM 语法中的 RDI = OFFSET my_array。 (当然,在 64 位模式下,您需要一个 RIP 相关的 LEA,例如 lea rdi, [my_array],但在 32 位模式下,您会 mov edi, OFFSET my_array。)


OS 负责确保 CS、DS、ES 和 SS 的段基地址为 0,因此 [ebx][ebp] 访问当 EBX = EBP 时,相同的线性地址。 (通过使用 EBX 或 EBP 作为寻址模式的基址寄存器隐含的隐式 DS 与 SS 段。)

并且 call ebx 将从您可以使用 mov dword ptr [ebx], 0C3909090(3x nop + ret)读取或写入的相同内存地址获取代码。


传统的 16 位实模式代码通常确实需要确保 DS 段基址设置为与数据段的开头相匹配。a 大型程序中的数据部分。**(在实模式下,mov ds, ax 写入段寄存器设置基数 = value<<4,而不是使用该值作为选择器索引the GDT 或 LDT。)

例如,DOS .exe 程序通常以

开头
PROC main
   mov ax, @data       ; segment for the data section
   mov ds, ax
   mov es, ax

  mov  bx, OFFSET other_var
  mov  ax, [bx]         ; relies on DS being set properly

  mov  [some_var], ax   ; also relies on DS; uses the offset of some_var in the addressing mode.
  ...

您可以 mov bx, OFFSET some_var 在设置 DS 之前,然后取消引用它。段和偏移部分是独立的。

同样,旧版 BIOS MBR 引导加载程序需要设置 DS 以匹配它们假定的 org 值,否则它们会访问内存中的错误位置。

超过64k数据的实模式程序需要多个段,并且必须切换DS或ES才能使用它们。与拥有任何代码都可以有效使用的通用 32 位指针相比,这通常是不方便的,而且 x86-16 已经完全过时了,因此人们很少编写实际使用 x86 分段的新程序。 如果您需要大量内存,编写 32 位或 64 位代码会容易得多。