【问题标题】:Pointer/memory arithmetic in Assembly汇编中的指针/内存算术
【发布时间】:2014-06-13 13:47:33
【问题描述】:

我正试图掌握汇编,但有一件可能非常简单的事情我不明白。

考虑下面这个简单的例子

long long * values = new long long[2];
values[0] = 10;
values[1] = 20;

int j = -1;

values[j+2] = 15;  // xxxxxxx

现在,最后一行(标有 xxxxxx)反汇编为:

000A6604  mov         eax,dword ptr [j]  
000A6607  mov         ecx,dword ptr [values]  
000A660A  mov         dword ptr [ecx+eax*8+10h],0Fh  

第一个问题:eax 和 ecx 中实际存储的内容是实际值(即“j”为 -1,“values”为两个 long long 值 10 和 20),还是仅仅是内存指向存储值的某个位置的地址(例如,像 &p、&values 之类的东西)?

第二个问题,我知道第三行应该做什么,但我不太确定为什么这真的有效。 所以我的理解是,它将值 0x0F 复制到指定的内存位置。内存位置基本上是 - 存储在 ecx 中的第一个元素的位置 - 加上 long long 字节的大小 (= 8) * eax 的值(等于 j,所以 -1) - 加上 16 字节的通用偏移量(大小为 long long 的 2 倍)。 我不明白的是:在这个表达式中, ecx 似乎是一个内存地址,而 eax 似乎是一个值(-1)。这怎么可能?看到它们的定义方式几乎相同,难道 eax 和 ecx 不应该都包含内存地址,或者两者都包含值吗?

谢谢。

【问题讨论】:

  • 内存地址和值都只是位。唯一的区别在于这些位代表什么。
  • 关于第一个问题:它分别加载了jvalues的值。 values 的值反过来是一块内存的地址。编写 mov ecx, OFFSET values 之类的内容就像在 C 中获取指针的地址,它会为您提供指向指针的指针。
  • 请注意,jvalues 在您的 C 代码中也有不同的类型。
  • 啊,当然,这是有道理的。 j = -1,但值 = 内存地址。我将值与 *values 混淆了。好的,我想这可以解释这两个问题,非常感谢!

标签: c++ assembly


【解决方案1】:

eaxecx 是寄存器——前两条指令将计算中使用的值加载到这些寄存器中,即jvalues(其中values 表示数组的基地址那个名字)。

我知道第三行应该做什么,但我不太确定为什么这真的有效

指令mov dword ptr [ecx+eax*8+10h],0Fh 表示将值0Fh(即十进制15)移动到位置ecx+eax*8+10h。要弄清楚这一点,请考虑每一部分:

  • ecxvalues 数组的基地址

  • eaxj 处的值,即-1

  • eax*8j 转换为以字节为单位的偏移量——long long 的大小为 8 个字节

  • eax*8+10h 10h 是十进制的 16,即 2*8,所以这是j+2 转换为字节偏移量

  • ecx+eax*8+10h 将最终偏移量添加到数组的基地址以确定存储值 15 的位置

【讨论】:

  • 很好的答案,谢谢!最后一个问题,它如何“知道” j(或分别为 eax)已签名? j 的定义是“mov dword ptr [j],0FFFFFFFFh”。因此,如果将 0xFFFFFFFF 解释为无符号数字,则 0xFFFFFFFF 等于 4,294,967,295(实际上,这就是 VC 中的监视窗口告诉我的内容)。但我假设 4,294,967,295 * 8 会导致溢出,或者产生任何东西,而不是预期的 -8。那么这是如何工作的呢?!
  • 0xFFFFFFFF * 80x7FFFFFFF8,确实太大而无法放入 32 位数字(即指针),因此只使用最低有效 32 位。这给我们留下了0xFFFFFFF8,即-8 十进制。加上2*8,你得到0x00000008,这是添加到values得到values[-1+2]的地址的正确数量。
  • 太好了,明白了。它怎么知道 0xFFFFFFF8 代表 -8,而不是 4,294,9672,88?例如。有符号整数 a = -1 = 0xFFFFFFFF,无符号整数 b = 4294967295 = 0xFFFFFFFF。但是访问 values[a+1] 和 values[b+1] 会发出相同的汇编代码;那么它如何知道是访问元素 0 还是元素 4,294,967,296?
  • 处理器不“知道”——它只是位。编译器(更具体地说,编写编译器的人)知道如何使用 2 的补码算法来实现负数。你可能需要玩一些例子才能真正理解。首先,确保您知道如何使用 2 的补码(即反转所有位并加 1)。示例:0x12345678 - 0x8 == 0x12345678 + 0xFFFFFFF8 == 0x112345670,如果限制为最低有效 32 位,则为 0x12345670
  • 似乎缺少的是'mov dword ptr [ecx+eax*8+14h],00h',将值[j+2]的上半部分归零,因为它很长长(64 位或 qword)。
猜你喜欢
  • 2021-08-26
  • 2017-01-27
  • 2021-08-23
  • 2017-10-23
  • 1970-01-01
  • 2015-02-16
  • 2010-10-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多