【问题标题】:What does this set of instructions do?这组指令有什么作用?
【发布时间】:2015-04-03 16:33:52
【问题描述】:

这组指令有什么作用?

   7ffff7a97759    mov    0x33b780(%rip),%rax        # 0x7ffff7dd2ee0
   7ffff7a97760    mov    (%rax),%rax
   7ffff7a97763    test   %rax,%rax
   7ffff7a97766    jne    0x7ffff7a9787a

我不知道这些说明会做什么,有人可以解释一下吗?

【问题讨论】:

  • 我认为“什么都不做”意味着不跳?它仍然可以做事
  • @PaulGriffiths 这听起来像是一个答案。
  • @Shade:同意,评论已删除。

标签: assembly x86-64 instructions


【解决方案1】:

一步一个脚印……

7ffff7a97759    mov    0x33b780(%rip),%rax        # 0x7ffff7dd2ee0

这个:

  1. 获取rip中的地址,并添加0x33b780。此时rip包含下一条指令的地址,即0x7ffff7a97760。将0x33b780 添加到其中会得到0x7ffff7dd2ee0,即评论中的地址。

  2. 它将存储在该地址的 8 字节值复制到 rax

让我们同意将这个 8 字节的值称为“指针”。根据地址的值,0x7ffff7dd2ee0 几乎可以肯定是堆栈上的一个位置。

7ffff7a97760    mov    (%rax),%rax

这会将存储在指针地址中的 8 字节值复制到 rax 中。

7ffff7a97763    test   %rax,%rax

这将执行 rax 与自身的按位与,丢弃结果,但修改标志。

7ffff7a97766    jne    0x7ffff7a9787a

如果按位与的结果不为零,则跳转到位置0x7ffff7a9787a,换句话说,如果存储在rax 中的值不为零。

所以总而言之,这意味着“找到存储在由rip 加上0x33b780 指示的指针中包含的地址处的8 字节值,如果该值不为零,则跳转到位置0x7fff7a9787a”。例如,在 C 语言中,存储在0x7ffff7dd2ee0 的指针可能是long *,这段代码检查它指向的long 是否包含0

它在 C 中的等价物可能类似于:

long l = 0;
long * p = &l;   /*  Assume address of p is 0x7ffff7dd2ee0  */


/*  Assembly instructions in your question start here  */

if ( *p == 0 ) {
    /*  This would be the instruction after the jne  */
    /*  Do stuff  */
}

/*  Location 0x7ffff7a9787a would be here, after the if block  */
/*  Do other stuff  */

这是一个完整的程序,展示了这个结构的使用,唯一的区别是我们找到了引用帧指针的指针,而不是指令指针:

.global _start

        .section .rodata

iszerostr:      .ascii  "Value of a is zero\n"
isntzerostr:    .ascii  "Value of a is not zero\n"

        .section .data

a:      .quad   0x00                    #  We'll be testing this for zero...

        .section .text

_start:
        mov     %rsp, %rbp              #  Initialize rbp
        sub     $16, %rsp               #  Allocate stack space
        lea     (a), %rax               #  Store pointer to a in rax...
        mov     %rax, -16(%rbp)         #  ...and then store it on stack

        #  Start of the equivalent of your code

        mov     -16(%rbp), %rax         #  Load pointer to a into rax
        mov     (%rax), %rax            #  Dereference pointer and get value
        test    %rax, %rax              #  Compare pointed-to value to zero
        jne     .notzero                #  Branch if not zero

        #  End of the equivalent of your code

.zero:
        lea     (iszerostr), %rsi       #  Address of string
        mov     $19, %rdx               #  Length of string
        jmp     .end

.notzero:
        lea     (isntzerostr), %rsi     #  Address of string
        mov     $24, %rdx               #  Length of string

.end:
        mov     $1, %rax                #  write() system call number
        mov     $1, %rdi                #  Standard output
        syscall                         #  Make system call

        mov     $60, %rax               #  exit() system call number
        mov     $0, %rdi                #  zero exit status
        syscall                         #  Make system call

带输出:

paul@thoth:~/src/asm$ as -o tso.o tso.s; ld -o tso tso.o
paul@thoth:~/src/asm$ ./tso
Value of a is zero
paul@thoth:~/src/asm$ 

顺便说一句,基于指令指针计算偏移量的原因是为了提高位置无关代码的效率,这对于共享库来说是必要的。硬编码内存地址和共享库不能很好地混合,但是如果您知道代码和数据之间的距离至少总是相同的,那么通过指令指针引用代码和数据可以让您轻松生成可重定位代码。如果没有这种能力,通常需要一个间接层,因为相关分支通常在范围内受到限制。

【讨论】:

  • 一个简单的问题:他们为什么要通过偏移指令指针来访问数据元素?这很常见吗?自从我以前在 386/486 等上编写 asm 代码以来,情况发生了很大变化。我觉得自己老了!感谢您非常出色的解释。
  • @BingBang:这是为了提高位置无关代码的效率,这对于共享库是必要的。硬编码内存地址和共享库不能很好地混合,但是如果您知道代码和数据之间的距离至少总是相同的,那么通过指令指针引用代码和数据可以让您轻松生成可重定位代码。我相信这种能力是在 x86_64 中引入的。如果没有这种能力,通常需要一个间接层,因为相对地址的范围是有限的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-13
  • 2015-05-15
  • 1970-01-01
  • 1970-01-01
  • 2018-08-25
  • 1970-01-01
相关资源
最近更新 更多