使用`esp*scale时错误寻址内存

Posted

技术标签:

【中文标题】使用`esp*scale时错误寻址内存【英文标题】:Error addressing memory when using`esp*scale 【发布时间】:2017-07-03 20:48:55 【问题描述】:

内存寻址一般形式(找到here)是:

[base + index*scale + disp]

当我尝试汇编/编译以下代码时:

mov eax, [ebx + esp*4 + 2]

NASM 给出以下错误:“错误:无效的有效地址”.

但以下工作正常:

 mov eax, [ebx + esp + 2]

以下也可以正常工作:

mov eax, [ebx + ecx*4 + 2]

所以看起来像使用scaleesp 作为索引寄存器会导致错误。

我说的对吗?我在哪里可以阅读更多相关信息(除了英特尔手册,它长达 4000 多页!)。

【问题讨论】:

你不能使用 ESP 作为索引,所以它不能被缩放。如果您考虑一下,扩展 ESP 是没有意义的,因为它总是包含一个地址。您可以在英特尔手册中名为“内存操作数”的部分阅读更多相关信息。 您无需阅读全部 4000 页。这是一个参考。你只需要图3-11。基本架构手册中的偏移(或有效地址)计算。见鬼,它还专门说“ESP寄存器不能用作索引寄存器”。最后,你还能相信什么?网上随便找几个人?而不是官方手册? @Ross Ridge "你不能使用 ESP 作为索引" 但是下面不是使用esp 作为索引寄存器吗:mov eax, [ebx + esp + 2] 汇编器足够聪明,可以让 ESP 成为基础,EBX 成为索引。 【参考方案1】:

Intel IA-32 Architecture manuals 很好地涵盖了这里的“规则”。特别是,Volume 1: Basic Architecture 包含以下信息:

3.7.5 指定偏移量

内存地址的偏移部分可以直接指定为静态值(称为位移)或通过由以下一个或多个组件组成的地址计算:

位移 — 8、16 或 32 位值。 Base — 通用寄存器中的值。 索引 — 通用寄存器中的值。 比例因子 — 乘以索引值的 2、4 或 8 值。

添加这些组件所产生的偏移量称为有效地址。这些组件中的每一个都可以具有正值或负值(2s 补码),但比例因子除外。图 3-11 显示了这些组件可以组合在一起以在所选段中创建有效地址的所有可能方式。

图 3-11。偏移量(或有效地址)计算

通用寄存器作为基址或索引组件的使用受到以下限制:

ESP 寄存器不能用作索引寄存器。 当ESP 或EBP 寄存器用作基址时,SS 段是默认段。在所有其他情况下,DS 分段是默认分段。

base、index 和displacement 组件可以任意组合使用,并且这些组件中的任何一个都可以为NULL。仅当还使用索引时才可以使用比例因子。每种可能的组合对于程序员在高级语言和汇编语言中常用的数据结构都很有用。

以下寻址模式建议使用地址组件的常见组合。

位移 ⎯ 单独的位移表示对操作数的直接(未计算)偏移。因为位移是在指令中编码的,所以这种形式的地址有时称为绝对地址或静态地址。它通常用于访问静态分配的标量操作数。 Base ⎯ 单独的base 表示对操作数的间接偏移。由于基址寄存器中的值可以变化,因此可以用于动态存储变量和数据结构。

Base + Displacement ⎯ 基址寄存器和位移可以一起用于两个不同的目的:

当元素大小不是 2、4 或 8 字节时作为数组的索引 - 位移组件将静态偏移量编码到数组的开头。基址寄存器保存计算结果,以确定数组中特定元素的偏移量。 要访问记录的字段:基址寄存器保存记录开头的地址,而位移是字段的静态偏移量。

这种组合的一个重要特殊情况是访问过程激活记录中的参数。过程激活记录是进入过程时创建的堆栈帧。这里,EBP 寄存器是基址寄存器的最佳选择,因为它会自动选择堆栈段。这是此常用功能的紧凑编码。

(Index ∗ Scale) + Displacement ⎯ 当元素大小为 2、4 或 8 字节时,此地址模式提供了一种对静态数组进行索引的有效方法。位移定位数组的开头,索引寄存器保存所需数组元素的下标,处理器通过应用缩放因子自动将下标转换为索引。

Base + Index + Displacement ⎯ 一起使用两个寄存器支持二维数组(位移保存数组开头的地址)或记录数组的几个实例之一(位移是记录中某个字段的偏移量)。 Base + (Index ∗ Scale) + Displacement ⎯ 当数组元素的大小为 2、4 或 8 字节时,将所有寻址组件一起使用可以有效地索引二维数组。

阅读并遵守这些规则,你会没事的。您需要阅读所有 4000 页,但您确实应该对手册的内容足够熟悉,以便在需要时查找内容(例如,当汇编程序发出错误消息时)。如果不熟悉微处理器的设计或编程模型,就很难对其进行编程。

mov eax, [ebx + esp*4 + 2]

确实是一种无效的寻址模式,因为您正在尝试使用 ESP 作为索引。图 3-11 表明您不能使用ESP 作为索引,正下方的第一个项目符号点也是如此。这里的逻辑是ESPstack p指针,它包含一个地址。

缩放地址没有意义。然而,从地址偏移 是有意义的,这就是为什么ESP 可以用作基础。这就是为什么这很好:

mov eax, [ebx + esp + 2]

这样也好:

mov eax, [ebx + ecx*4 + 2]

因为ECX可以用作索引。

在 cmets 中,您提出以下指令:

mov eax, [ebx + esp + 2]

正如 Ross Ridge 已经回答的那样,这是合法的,因为您实际上并没有缩放ESP,所以汇编器足够聪明,可以使用ESP 作为基础EBX 作为索引。换句话说,它已将其重新排序为:

mov eax, [2 + esp + ebx]
          ^    ^     ^
          |    |     |
displacement   |    index
              base

以隐式 1 作为比例因子。

【讨论】:

以上是关于使用`esp*scale时错误寻址内存的主要内容,如果未能解决你的问题,请参考以下文章

esp寻址ebp寻址

ESP8266 NodeMCU Lua 如何清除堆?

ESP8266编译时错误

x86---32汇编---内存寻址模式

EBP循址

调用 API 函数时出现 ESP 错误?