首页 > 解决方案 > NASM:通过寄存器从内存移动到内存

问题描述

我很难将数据从内存移动到另一个内存bss。但是,当移动一些奇怪的字符出现在前几个字节中并且我的字符串的一半也丢失而另一半很好时,我的实现有些工作。

这是我打印出来时得到的值message==�F@elcome to the new life

我需要所有的帮助,我错过了什么?我检查了我的代码一百次。

    section .data
hello:       db  "Hello, Welcome to the new life! Lets begin the journey.",10
hello_len:   equ  $ - hello

    section .bss
message: resb 255

    section .text

mov rdi, hello
mov rsi, message

msg_into_message:
    cmp byte [rdi], 10          ; hello ends with a new line char
    je end_count
    mov al, byte [rdi]
    mov byte [rsi], al
    inc rsi
    inc rdi
    jmp msg_into_message

end_count:
    mov [message], rsi
    ret

    ; Prints message
    mov rsi, message
    mov rdx, hello_len
    call pre_print
    syscall

标签: assemblyx86-64nasm

解决方案


好的,首先,and sind代表源rsi目标可能会以另一种方式工作(正如你所拥有的那样),但你会让很多像我这样的 CDO 人感到不安(a) :-)rdi

但是,对于您的实际问题,请看这里:

end_count:
    mov [message], rsi

认为这意味着将最后一个字节复制0x10到目标中,但有两个问题:

  1. message是缓冲区的开始,而不是字节应该去的位置。
  2. 您将多字节rsi变量复制到那里,而不是您需要的字节。

正如您的症状所暗示的那样,这两点意味着您在前几个字节中添加了一些奇怪的值。

也许更好的方法如下:

    mov rsi, hello            ; as Gordon Moore intended :-)
    mov rdi, message

put_str_into_message:
    mov al, byte [rsi]        ; get byte, increment src ptr.
    inc rsi

    mov byte [rdi], al        ; put byte, increment dst ptr.
    inc rdi

    cmp al, 10                ; continue until newline.
    jne put_str_into_message

    ret

为了完整起见,如果您希望复制换行符(尽管这几乎是您现在所拥有的,只是mov消除了错误的缓冲区损坏)(b)

put_str_into_message:
    mov al, byte [rsi]        ; get byte.
    cmp al, 10                ; stop before newline.
    je  stop_str

    mov byte [rdi], al        ; put byte, increment pointers.
    inc rsi
    inc rdi

    jmp put_str_into_message

stop_str:
    ret

(a) CDO是强迫症,但字母排列正确:-)


(b)或者不复制换行循环可以更有效地完成,同时在底部仍然有一个分支。

一次循环一个字节仍然非常低效(x86-64SSE2可以让您一次复制和检查 16 个字节)。由于您将长度作为 assemble-time constant hello_len,您可以使用它来有效地复制宽块(如果您的缓冲区大小不是 16 的倍数,最后可能需要特殊处理),或者使用rep movsb.

但这展示了一个有效的循环结构,避免了将新的合并AL到底部的错误依赖RAX,允许乱序 exec 提前运行并更早地“看到”循环退出分支。

strcpy_newline_end:
    movzx  eax, byte [rsi]    ; get byte (without false dependency).
    cmp    al, 10
    je    copy_done           ; first byte isn't newline, enter loop.

copy_loop:                    ; do:
    mov    [rdi], al          ;    put byte.
    inc    rsi                ;    increment both pointers.
    inc    rdi
    movzx  eax, byte [rsi]    ;    get next byte.
    cmp    al, 10
    jne   copy_loop           ; until you get a newline.

; After falling out of the loop (or jumping here from the top)
; we've loaded but *not* stored the terminating newline

copy_done:
    ret

您还应该知道,您还可以使用其他技巧来将指令保存在循环中,例如相对于另一个字符串寻址(使用索引寻址模式进行加载,只增加一个指针)。

但是,我们不会在这里详细介绍它们,因为它可能会使答案变得比它需要的更复杂。


推荐阅读