为啥 gdb 在 RIP 相对模式下显示与绝对地址不同的地址?

Posted

技术标签:

【中文标题】为啥 gdb 在 RIP 相对模式下显示与绝对地址不同的地址?【英文标题】:Why gdb shows different addresses in RIP-relative mode from absolute address?为什么 gdb 在 RIP 相对模式下显示与绝对地址不同的地址? 【发布时间】:2022-01-21 13:11:12 【问题描述】:

c:

#include <stdio.h>
#include <stdlib.h>
int x;
int main()
    printf("eneter x\n");   
    scanf("%i",&x);
    printf("you enetered: %i\n", x);
    return 0;

在 gdb 中:

starti
disas main

0x0000555555555155 <+0>:    push   %rbp
   0x0000555555555156 <+1>: mov    %rsp,%rbp
   0x0000555555555159 <+4>: lea    0xea4(%rip),%rdi        # 0x555555556004
   0x0000555555555160 <+11>:    callq  0x555555555030 <puts@plt>
   0x0000555555555165 <+16>:    lea    0x2ed8(%rip),%rsi        # 0x555555558044 <x>
   0x000055555555516c <+23>:    lea    0xe9a(%rip),%rdi        # 0x55555555600d
   0x0000555555555173 <+30>:    mov    $0x0,%eax
   0x0000555555555178 <+35>:    callq  0x555555555050 <__isoc99_scanf@plt>
   0x000055555555517d <+40>:    mov    0x2ec1(%rip),%eax        # 0x555555558044 <x>
   0x0000555555555183 <+46>:    mov    %eax,%esi
   0x0000555555555185 <+48>:    lea    0xe84(%rip),%rdi        # 0x555555556010
   0x000055555555518c <+55>:    mov    $0x0,%eax
   0x0000555555555191 <+60>:    callq  0x555555555040 <printf@plt>
   0x0000555555555196 <+65>:    mov    $0x0,%eax
   0x000055555555519b <+70>:    pop    %rbp
   0x000055555555519c <+71>:    retq 

这里x变量的相对地址是$rip+0x2ed8(来自指令lea 0x2ed8(%rip),%rsi # 0x555555558044)。但正如您在评论# 中看到的那样,绝对 地址是0x555555558044。好的,当我尝试从亲戚那里读取时,我会得到那个地址吗?让我们看看:

x $rip+0x2ed8
0x555555558055: 0x00000000

nop - 相对地址没有使用绝对地址,x var 是真正存储的 (0x555555558055!= 0x555555558044) 差异是 17 个字节。是指令本身的字节数(lea + 操作数)吗?我不知道,但不这么认为。那么为什么 gdb 中的相对寻址和绝对寻址不同呢?

PS,生成的程序集:

.file   "a.c"
    .comm   x,4,4
    .section    .rodata
.LC0:
    .string "eneter x"
.LC1:
    .string "%i"
.LC2:
    .string "you enetered: %i\n"
    .text
    .globl  main
    .type   main, @function
main:
    pushq   %rbp    #
    movq    %rsp, %rbp  #,
# a.c:5:    printf("eneter x\n");   
    leaq    .LC0(%rip), %rdi    #,
    call    puts@PLT    #
# a.c:6:    scanf("%i",&x);
    leaq    x(%rip), %rsi   #,
    leaq    .LC1(%rip), %rdi    #,
    movl    $0, %eax    #,
    call    __isoc99_scanf@PLT  #
# a.c:7:    printf("you enetered: %i\n", x);
    movl    x(%rip), %eax   # x, x.0_1
    movl    %eax, %esi  # x.0_1,
    leaq    .LC2(%rip), %rdi    #,
    movl    $0, %eax    #,
    call    printf@PLT  #
# a.c:8:    return 0;
    movl    $0, %eax    #, _6
# a.c:9: 
    popq    %rbp    #
    ret 
    .size   main, .-main
    .ident  "GCC: (Debian 8.3.0-6) 8.3.0"
    .section    .note.GNU-stack,"",@progbits

这里使用的是相对于RIP的模式

# a.c:6:    scanf("%i",&x);
    leaq    x(%rip), %rsi   #,

xx 符号的位置。但是在cmets中,有人说$rip+0x2ed8不一样,偏移0x2ed8不会导致x的地址。但是为什么这两个不同呢?但应该是 RIP 相对模式寻址,并且两者都应该获得相同的偏移量(因此地址)。

【问题讨论】:

评论不用于扩展讨论;这个对话是moved to chat。 【参考方案1】:
   0x0000555555555165 <+16>:    lea    0x2ed8(%rip),%rsi        # 0x555555558044 <x>
   0x000055555555516c <+23>:    lea    0xe9a(%rip),%rdi        # 0x55555555600d

指令中的 RIP 相对地址是相对于当前指令之后的地址(即指令的地址加上指令的大小,或下一条指令的地址)。这是因为当指令被加载到处理器中时,RIP 寄存器在它执行之前会增加当前指令的大小。 (至少这是遵循的模型,即使现代处理器在幕后使用各种技巧来加速执行。)(注意:上述情况适用于几种 CPU 架构,包括 x86 变体,但其他一些 CPU 架构不同在测量 PC 相对地址的点1。)

上面的第一条指令在地址 0x555555555165 处,下面的指令在地址 0x55555555516c 处(指令长 7 个字节)。在第一条指令中,RIP相对地址0x2ed8(%rip)指的是0x2ed8 + 0x000055555555516c = 0x555555558044。

请注意,如果您在调试器中对一条指令设置断点并在到达断点时显示寄存器,RIP 将指向当前指令,而不是下一条指令,因为当前指令尚未执行。


1感谢 Peter Cordes 提供有关 ARM 和 RISC-V CPU 架构的 PC 相对寻址的详细信息。

【讨论】:

如果我不知道来源,如何确定下一条指令的大小 @autistic456 x86 指令长度可变,取决于寻址模式。您需要查阅处理器的指令集参考以确定指令的大小。我已经更改了答案,以明确何时更新 RIP 寄存器。 Abbot,RIP会指向当前指令,而不是下一条,因为当前指令还没有被执行,调试器中rip显示当前指令,因为调试器显示尚未执行,但在程序流程中,“真实”rip 显示下一条指令,因为内联执行。那么调试器中的 rip 具有误导性,因为我理解执行前的停止,但我现在有 2 个 rip 引用,它们有 2 个含义(调试器 rip 和“真实” - 可执行文件 rip),是吗功能或错误? @autistic456 是的,一开始这很令人困惑,但大多数处理器都是这样做的。 (我想不出有什么不这样做的。)对于使用固定大小指令的处理器来说,处理起来更容易! RISC 处理器设计使用固定指令长度很常见,尽管其中一些后来引入了特殊模式来执行压缩指令集(例如 ARM“Thumb”模式)以节省内存。

以上是关于为啥 gdb 在 RIP 相对模式下显示与绝对地址不同的地址?的主要内容,如果未能解决你的问题,请参考以下文章

gdb 在相对于基地址的地址处打印符号

如何在 64 位汇编程序中使用 RIP 相对寻址?

如何在 64 位汇编程序中使用 RIP 相对寻址?

绝对路径与相对路径问题

NASM x86_64在32位模式下组装:为什么该指令产生RIP相对寻址代码?

HTML相对路径与绝对路径~~~~~~遇到的问题..为啥会这样?