这条指令有什么作用( REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS: )?

use*_*535 5 x86 assembly instructions

我正在查看 Intel-x86 程序跟踪并遇到此指令

REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:
Run Code Online (Sandbox Code Playgroud)

我知道

REP MOVS
Run Code Online (Sandbox Code Playgroud)

导致 MOV 指令运行由 ECX 寄存器中的值指定的次数,在我的例子中是 0x2b。

我知道

BYTE PTR
Run Code Online (Sandbox Code Playgroud)

正在确定信息的大小,在这种情况下只是一个字节。

我知道

ES:[EDI]
Run Code Online (Sandbox Code Playgroud)

告诉将 BYTE PTR DS: 中的任何内容移动到 EDI 指向的地址。

我不知道逗号后面的部分是做什么的。

BYTE PTR DS:
Run Code Online (Sandbox Code Playgroud)

问题:

PTR指令为什么会这样?为什么不只是

REP MOVS BYTE ES:[EDI]. BYTE DS:
Run Code Online (Sandbox Code Playgroud)

ES和DS对应的是什么?

谢谢

vin*_*est 5

指令似乎并没有就此结束。我今天刚刚在 OllyDBG 中遇到了这个指令,我可以调整指令列的大小以显示指令的其余部分。

00499B3A  |. F3:A4          |REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
Run Code Online (Sandbox Code Playgroud)

现在我们知道完整的指令是,但我仍然不知道这个指令是做什么的。所以我从这里拿出了英特尔指令集参考手册并搜索了操作码F3:A4

在手册中,它描述了这个操作码如下:

将 (E)CX 字节从 DS:[(E)SI] 移动到 ES:[(E)DI]。


Max*_*tin 5

汇编器别名

REP MOVS DWORD PTR ES:[EDI], DWORD PTR [ESI]

是同义词 REP MOVSD

REP MOVS BYTE PTR ES:[EDI], BYTE PTR[ESI]

是同义词

REP MOVSB

您可以通过这种方式来尝试“提高”代码的可读性。想法可能如下:“如果有人忘记了 MOVSB 从 ESI 转移到 EDI,这种更长的语法将有助于使事情更清晰”。这绝不会影响编译后的二进制形式。区别仅在于文本源代码。

如您所知,根据数据大小,有以下 MOVS 命令:

  • MOVSB(字节,8 位)
  • MOVSW(字,16 位)
  • MOVSD(双字,32 位)
  • MOVSQ(qword,64 位) - 仅在 64 位模式下可用

这个指令有什么作用

MOVS 命令将数据从 DS:SI(ESI/RSI) 复制到 ES:DI(EDI/RDI) -- SI/DI 寄存器的大小取决于您当前的模式 - 16 位、32 位或 64 位. 它还增加(减少)SI 和DI 寄存器(基于D 标志,设置CLD 以增加寄存器)。

MOVS 命令不能使用除 DS:SI/ES:DI 之外的其他寄存器,因此无需指定它们。在我看来,设置它们甚至是多余的,可读性不仅没有提高反而变差了。

关于段寄存器 ES 和 DS

DS 和 ES 是“段”寄存器。正如我之前写过的,MOVS 只使用 SI/DI 作为索引寄存器和 DS/ES 作为段寄存器。您不能修改 MOVS 命令使用的寄存器。

但是你不应该担心段寄存器,因为它们通常已经正确设置,如果你的程序在 Linux、Windows 等标准操作系统下运行,你不应该修改或关心它们。这些段寄存器可能只需要在以下情况下:

  1. 您正在为 16 位模式编写程序,例如 MS-DOS 16 位实模式或 MS-DOS 16 位保护模式(在 80286 上可用)。
  2. 您正在为新操作系统编写内核/管理程序,或者您的应用程序在没有任何操作系统的裸机硬件上运行。

在 16 位模式下,在从 8086 到 80286 的 Intel CPU 上,有以下段寄存器:CS DS ES SS。

在实模式下,16 位段寄存器被解释为线性 20 位地址的最高有效 16 位(因此 CPU 确实将段寄存器的值乘以 16 以获得段的基地址) . 例如,如果将 1 移至 DS,并将 2 移至 SI,则“byte ptr DS:[SI]”将表示 1*16+2 = 18(从内存空间开始的第 18 个字节)。

在保护模式(80286 及以上)下,段寄存器不再保存 16 位整数值。它们现在包含一个指向包含 24 位基地址的段描述符表的索引。

在 Intel 80386 及更高版本中,32 位保护模式保留了 80286 保护模式的分段机制。仍然添加了分页单元作为分段单元和物理总线之间地址转换的第二层。此外,每个段描述符中的段基也是 32 位(而不是 24 位)。此外,还添加了两个新的段寄存器:FS 和 GS。

64 位架构不使用分段。四个段寄存器:CS、SS、DS 和 ES 强制为 0,限制为 264。段寄存器 FS 和 GS 仍然可以具有非零基地址。这允许操作系统将这些段用于特定目的。

一个值得注意的事实是 386 和更高版本的 Intel x86 CPU 仍然使用 16 位大小的段寄存器,因为它们仅保存段描述符表的索引。

正如我之前所写,在标准操作系统中,无论是 32 位还是 64 位,段寄存器都是已经预先配置的 DS 和 ES 寄存器并指向相同的内存,因此您可以忽略它们。

您可以在英特尔® 64 位和 IA-32 架构软件开发人员手册(组合卷:1、2A、2B、2C、2D、3A、3B、3C、3D 和 4)的第 7.3.9.1 章“字符串指令”中找到更多信息)。引用:

这些指令对字符串中的单个元素进行操作,字符串可以是字节、字或双字。要操作的字符串元素由 ESI(源字符串元素)和 EDI(目标字符串元素)寄存器标识。这两个寄存器都包含指向字符串元素的绝对地址(段中的偏移量)。默认情况下,ESI 寄存器寻址由 DS 段寄存器标识的段。段覆盖前缀允许 ESI 寄存器与 CS、SS、ES、FS 或 GS​​ 段寄存器相关联。EDI 寄存器寻址由 ES 段寄存器标识的段;EDI 寄存器不允许段覆盖。在字符串指令中使用两个不同的段寄存器允许对位于不同段中的字符串执行操作。或者通过将 ESI 寄存器与 ES 段寄存器相关联,源字符串和目标字符串都可以位于同一段中。(后一种情况也可以通过使用相同的段选择器加载 DS 和 ES 段寄存器并允许 ESI 寄存器默认为 DS 寄存器来实现。)MOVS 指令将 ESI 寄存器寻址的字符串元素移动到寻址的位置通过 EDI 寄存器。汇编程序识别此指令的三种“短格式”,它们指定要移动的字符串的大小:MOVSB(移动字节字符串)、MOVSW(移动字字符串)和 MOVSD(移动双字字符串)。(后一种情况也可以通过使用相同的段选择器加载 DS 和 ES 段寄存器并允许 ESI 寄存器默认为 DS 寄存器来实现。)MOVS 指令将 ESI 寄存器寻址的字符串元素移动到寻址的位置通过 EDI 寄存器。汇编程序识别此指令的三种“短格式”,它们指定要移动的字符串的大小:MOVSB(移动字节字符串)、MOVSW(移动字字符串)和 MOVSD(移动双字字符串)。(后一种情况也可以通过使用相同的段选择器加载 DS 和 ES 段寄存器并允许 ESI 寄存器默认为 DS 寄存器来实现。)MOVS 指令将 ESI 寄存器寻址的字符串元素移动到寻址的位置通过 EDI 寄存器。汇编程序识别此指令的三种“短格式”,它们指定要移动的字符串的大小:MOVSB(移动字节字符串)、MOVSW(移动字字符串)和 MOVSD(移动双字字符串)。

关于MOVS指令的性能

自从 1993 年生产出第一款 Pentium CPU 以来,Intel 开始使简单命令更快,而复杂命令(如 REP MOVS)则更慢。

因此,REP MOVS 变得非常缓慢,并且没有更多实际理由使用它。

2013 年,英特尔决定重新审视 REP MOVS。如果 CPU(2013 年之后生产)具有 CPUID ERMSB(增强型 REP MOVSB)位,则 REP MOVSB 和 REP STOSB 命令的执行方式与旧处理器不同,并且应该很快。在实践中,它只对大块、256 字节及更大的块快速,并且只有在满足某些条件时:

  • 源地址和目标地址都必须与 16 字节边界对齐;
  • 源区域不应与目的区域重叠;
  • 长度必须是 64 的倍数才能产生更高的性能;
  • 方向必须是向前的 (CLD)。

请参阅英特尔优化手册,第 3.7.6 节增强型 REP MOVSB 和 STOSB 操作 (ERMSB) http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia- 32-architectures-optimization-manual.pdf

由于启动成本非常高——大约 35 个周期,它们在小块上非常慢。