如何解决数组的偏移量是如何表示数组的起始地址的
考虑汇编代码 - mov edi,offset newarray
根据我读过的内容,这会将 newarray
的地址放入寄存器 edi
我不明白这个词offset
这个词的英文意思是什么
适合这里。
解决方法
x86 内存地址的形式为 segment:offset
,其中偏移量部分位于普通(通用)寄存器中,如 EDI(或 64 位代码中的 RDI)。
在我们使用平面内存模型的现代系统中,segment
基数始终为 0,而 offset
是整个地址,等于线性地址。
mov eax,[rdi + rax*4 + 1234]
的 x86-64 寻址:
-
RDI 是 addressing mode 中的 基址寄存器。
- RAX 是索引(因为它具有比例因子,或
[rdi+rax]
中的第二个寄存器) - 1234 是位移,编码为双字 (disp32) 符号扩展为 64 位
- RAX 是索引(因为它具有比例因子,或
-
RDI + RAX*4 + 1234
是该寻址模式计算出的有效地址。 (因此,即使 FS 段基数非零,lea eax,fs:[rdi + rax*4 + 1234]
也会为您提供。)这是 seg:off 地址的 offset 部分。 -
RDI 作为基址寄存器意味着 DS 段。
seg:off
-> 线性地址是DS_base + offset
。 (这等于偏移量,因为 CS、DS、ES 和 SS 基在 64 位模式下固定为 0,而在 32 位模式下主流操作系统将它们设置为 0。) -
线性地址是一个虚拟地址,因为 64 位模式需要启用分页。
-
virt->phys 转换是通过在 TLB 缓存的页表中查找页码部分(底部 12 位以上的位)来实现的。 (Why in x86-64 the virtual address are 4 bits shorter than physical (48 bits vs. 52 long)?)。生成的物理地址用于访问内存(通过缓存),如果 phys 地址是设备地址,则用于访问 PCIe 上的 MMIO。
所以是的,要索引数组,您需要 MASM 语法中的 RDI = OFFSET my_array
。 (当然,在 64 位模式下,您需要像 lea rdi,[my_array]
这样的 RIP 相对 LEA,但在 32 位模式下,您需要 mov edi,OFFSET my_array
。)
操作系统负责确保 CS、DS、ES 和 SS 的段基地址为 0
,因此当 EBX = 时 [ebx]
和 [ebp]
访问相同的线性地址EBP。 (使用 EBX 或 EBP 作为寻址模式的基址寄存器隐含的 DS 与 SS 段。)
并且 call ebx
会从您可以使用 mov dword ptr [ebx],0C3909090
读取或写入的相同内存地址获取代码(3x nop + ret)。
传统的 16 位实模式模式代码通常需要确保 DS 段基址。(在实模式下,mov ds,ax
写入段寄存器设置基址 = { {1}},而不是将该值用作 GDT 或 LDT 中的选择器。)
例如,DOS value<<4
程序通常以
.exe
您可以在设置 DS 之前 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.
...
,然后取消引用它。段和偏移部分是独立的。
同样,传统 BIOS MBR 引导加载程序需要将 DS 设置为与它们假定的 mov bx,OFFSET some_var
值匹配,否则它们会访问内存中的错误位置。
具有超过 64k 数据的实模式程序需要多个段,并且必须切换 DS 或 ES 才能使用它们。与拥有任何代码都可以有效使用的通用 32 位指针相比,这通常很不方便,而且 x86-16 已经完全过时了,因此人们很少编写真正使用 x86 分段的新程序。 如果您需要大量内存,则编写 32 位或 64 位代码要容易得多。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。