【问题标题】:Does incrementing rsp is equal to pop?增加rsp是否等于pop?
【发布时间】:2020-07-26 18:42:01
【问题描述】:

我是汇编新手,我想编写一个函数来打印汇编中的数字并从 c++ 调用它,请参见下面的代码

cpp:

#include <iostream>
#include <stdint.h>

extern "C" void printunum(uint64_t);

int main()
{
    printunum(12345);
    std::cout<<std::endl;
    return 0;
}

组装:

   global printunum
section .text
printunum:
   mov rax,rdi
   mov rdi,10
   mov rsi,0

while1:
   cmp  rax,0
   je   endwhile1
   mov  rdx,0
   div  rdi
   inc  rsi
   add  rdx,48
   push rdx
   jmp  while1
endwhile1:

   mov r10,rsi

while2:
   cmp r10,0
   je  endwhile2
   mov rax,1
   mov rdi,1
   mov rsi,rsp  ;to pass memory address 
   add rsp,8    ;it is equal to pop or it is wrong and it will add 8 bytes here?
   mov rdx,1
   syscall
   dec r10
   jmp while2
endwhile2:
   ret

编辑的程序集:

      global print_uint64
section .text
    print_uint64:

   ;init
      mov rax,rdi
      mov rdi,10
      mov rsi,rsp
   ;endinit

   while:
      xor  rdx  ,rdx
      div  rdi
      add  rdx  ,48
      dec  rsi
      mov  [rsi],dl
      cmp  rax  ,0
      je   else 
      if: 
      jmp  while
      else:
   endwhile: 

   ;print
      mov rax,1
      mov rdi,1
      mov rdx,rsp
      sub rdx,rsi
      syscall
   ;endprint 

   ;return
      mov rax,rsp
      sub rax,rsi
      ret

我编译:

srilakshmikanthanp@HP-245-G5-NOTEBOOK-PC:~/Documents/Learn$ nasm -f elf64 asm.asm
srilakshmikanthanp@HP-245-G5-NOTEBOOK-PC:~/Documents/Learn$ g++ main.cpp asm.o -o main 
srilakshmikanthanp@HP-245-G5-NOTEBOOK-PC:~/Documents/Learn$ ./main
12345
srilakshmikanthanp@HP-245-G5-NOTEBOOK-PC:~/Documents/Learn$

我的处理器 amd64(x86-x64) 我正在使用 kali linux

1)上面的代码是好的还是错的(在x64位机器中pop等于rsp+8)。

2)mul 操作将结果放入 rdx:rax 我如何将其放入单个内存中

3)div 操作从 rdx:rax 中获取红利我如何将单个值放入 rdx:rax。 感谢您的答复。

【问题讨论】:

  • 这段代码效率非常低(将每个字符存储在一个单独的 qword 中,并对每个数字进行单独的系统调用),但它没有错。好吧,除了在 C 中实际声明 arg 时将其视为无符号 64 位之外,仅采用 32 位 int;通常调用者会写入一个 32 位的寄存器,因此它会隐式零扩展为 64 位。
  • 不需要将值加载到寄存器中,只需将 RSI 指向它,所以是的,只需要 pop 的 rsp+=8 部分。它可以只使用add rsi,8 并在最后恢复 RSP,在循环之外,而不是使用 2 条指令。但显然这段代码根本不关心效率。它还在循环的顶部使用cmp/je,而不是像普通的asm 循环那样使用底部。这意味着如果你传递它0,它将不打印数字而不是'0'。更简单的代码见How do I print an integer in Assembly Level Programming without printf from the c library?
  • @Peter Cordes 在你 cmets 之后我更改了我的代码中的一些内容,我将在编辑后的程序集中添加你能看到它以及存储在 rdx:rax 中的 mul 的结果如何将它存储到单个内存和 div 操作需要rdx:rax 来划分我如何将一个值放入 rdx:rax 你能解释一下吗?
  • 你在乘以计算如何恢复RSP?不要将mul 用于 2 的幂,只需左移即可。或者更好的是,将原始 RSP 保存在另一个寄存器中,这样您就可以 mov 而不是计算任何东西。此外,您现在正在打印每个数字之间有 7 个字节的零的缓冲区。大多数程序都会对此感到窒息,尽管它在终端上看起来是正确的。但是通过管道将其输入lesshexdump -C 以查看您实际输出的内容。首先不要使用push 来存储8 个字节,只需使用mov 并减少一个指针,就像我的链接答案中一样。
  • @Peter Cordes 根据您的建议和答案,我现在在编辑的程序集中进行了更改。你能看到吗

标签: assembly x86-64 callstack


【解决方案1】:

此示例已对以前的 cmets 提出了一些建议,并且比您的版本短 24 个字节。还有一些数学已经完成,因为 RBXRDI 之间的区别与在 R10 中使用计数器相同。

虽然使用 push 构建字符串是一个新颖的想法,但正如所指出的,在某些系统上,每个字符 7 个 NULL 可能会导致问题,而且不仅会导致不必要的大缓冲区。

    global printnum
    section .text

printnum:

; The maximum signed 64 bit value ( 9223372036854775807 ) needs a maximum of 19 digits or
; 20 for unsigned values. Create a buffer on the stack suitably large enough for that.

    push    rbp
    mov     rbp, rsp
    sub     rsp, 32         ; Reserve 32 bytes so stack stays QWORD aligned.

    mov     rax, rdi        ; Move value passed by caller to be converted.
    mov     rdi, rbp        ; Establish pointer to next byte past EOS (End of String).
    mov     ecx, 10         ; Divisor

LtoA:
    xor     edx, edx
    div     rcx
    or       dl, '0'
    dec     rdi             ; This way RDI always points to most recent character written.
    mov     [rdi], dl       ; Essentially DL = RAX % 10.
    test    rax, rax
    jnz     LtoA

showString:
    mov      al, 1          ; Equivalent to SYS_WRITE.
    mov     rsi, rdi        ; RSI points to beginning of string.
    mov     edi, eax        ; Equivalent to STDOUT.
    mov     rdx, rbp
    sub     rdx, rsi        ; Number of byte to be displayed.
    syscall

    leave                
    ret

【讨论】:

    【解决方案2】:

    POP 所做的是:

    • 将数据从[RSP]移动到目标寄存器
    • 将寄存器大小添加到RSP

    所以如果你不需要实际使用堆栈中的数据,你可以将数据的大小添加到RSP,效果是一样的

    【讨论】:

    • rsp+8 这里 8 是右字节,递增指针或内存将以字节为单位增加?
    • 你能看到 rdx:rax 中存储的 mul 的编辑程序集和结果如何将其存储到单个内存中,并且 div 操作需要 rdx:rax 来划分我如何将一个值放入 rdx:rax 你能解释一下。
    • 为什么将“寄存器大小”添加到 RSP 中?
    • @Kaushik 由于大量数据是从堆栈中获取的,因此必须正确调整堆栈指针。如果弹出一个 16 位寄存器,则必须将其移动 2。如果是 32 位,则移动 4 字节等。
    猜你喜欢
    • 1970-01-01
    • 2011-06-12
    • 1970-01-01
    • 1970-01-01
    • 2021-05-02
    • 2011-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多