How to implement memmove, not just memcpy, in assembly?

723 Views Asked by At

I was trying to implement memmove from C into x86 assembly so I wrote:

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

end:

But this is wrong, why? according to: http://www.cplusplus.com/reference/cstring/memmove/

Copies the values of num bytes from the location pointed by source to the memory block pointed by destination. Copying takes place as if an intermediate buffer were used, allowing the destination and source to overlap.

which my code doesn't support.

How can I fix this without using stack? Note: we can assume that immediately after source destination comes in memory and that num (number of bytes to copy) is far and can't be touched by wrong.

1

There are 1 best solutions below

9
On

The problem here is about potential overlapping in case destination - source < size (that is, both source and destination point to the same chunk of data). When this happens, you are in a situation like this:

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

If you start copying from source, you will overwrite part of what you are trying to copy (in this example you will overwrite the Bs with As), losing the original values, ending up with something like this:

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

When in reality you want this:

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

You can solve this issue by checking when destination - source < num, and copying in reverse in such case (starting with eax = num).

The corresponding assembly would be something like this:

    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: