如何在汇编中实现 memmove,而不仅仅是 memcpy?

问题描述

我试图将 memmove 从 C 实现到 x86 程序集,所以我写道:

start:
    movb source(%eax),%cl
    movb %cl,destination(%eax)
    inc %eax
    cmp num,%eax
    jne start

end:

但这是错误的,为什么呢?根据:http://www.cplusplus.com/reference/cstring/memmove/

将 num 个字节的值从 source 指向的位置复制到 目标指向的内存块。 复制就好像 使用了中间缓冲区允许目的地和 来源重叠。

我的代码不支持

如何在不使用堆栈的情况下解决此问题? 注意:我们可以假设源目标刚进入内存并且num(要复制的字节数)很远并且不会被错误触及。

解决方法

这里的问题是关于 destination - source < size 情况下的潜在重叠(即 sourcedestination 指向同一块数据)。发生这种情况时,您的情况如下:

AAAAAAAAAAAAAABBBBBBBBBBBBBB_______________________
^             ^             ^             ^
source        destination   source        destination
                             + num        + num

如果您从 source 开始复制,您将覆盖您要复制的部分内容(在本例中,您将用 B 覆盖 A),丢失原始值,最后是这样的:

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA_________
^             ^             ^             ^
source        destination   source        destination
                             + num        + num

实际上你想要这个时:

AAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBB_________
^             ^             ^             ^
source        destination   source        destination
                             + num        + num

您可以通过检查何时 destination - source < num 来解决此问题,并在这种情况下反向复制(以 eax = num 开头)。

相应的程序集应该是这样的:

    mov  $destination,%eax
    sub  $source,%eax             # dst-src distance in bytes
    cmp  num,%eax
    jb  backwards                  # if (dst-src < copy_size) goto backwards

forward:
    mov  $0,%eax
forward_loop:
    movb source(%eax),%cl
    movb %cl,destination(%eax)
    inc %eax
    cmp num,%eax
    jne forward_loop
    jmp end

backwards:
    movl  num,%eax        # start from i=length
backwards_loop:
    movb  source-1(%eax),%cl
    movb  %cl,destination-1(%eax)
    dec   %eax
    jnz   backwards_loop

end: