【问题标题】:Why does gcc use a relative address to the function pointer in assembly? [duplicate]为什么 gcc 在汇编中使用函数指针的相对地址? [复制]
【发布时间】:2019-07-10 17:54:33
【问题描述】:

C 源代码:

int sum(int a, int b) {    
    return a + b;    
}    

int main() {    
    int (*ptr_sum_1)(int,int) = sum;   // assign the address of the "sum" 
    int (*ptr_sum_2)(int,int) = sum;   // to the function pointer 
    int (*ptr_sum_3)(int,int) = sum;    

    int a = (*ptr_sum_1)(2,4);   // call the "sum" through the pointer 
    int b = sum(2,4);            // call the "sum" by usual way

    return 0;    
}

汇编代码的关键部分:

lea rax, sum[rip]
mov QWORD PTR -24[rbp], rax
lea rax, sum[rip]
mov QWORD PTR -16[rbp], rax
lea rax, sum[rip]
mov QWORD PTR -8[rbp], rax

来自 GDB 的执行程序指令:

   0x5fa <sum>: push   rbp
   0x5fb <sum+1>:   mov    rbp,rsp
   0x5fe <sum+4>:   mov    DWORD PTR [rbp-0x4],edi
   0x601 <sum+7>:   mov    DWORD PTR [rbp-0x8],esi
   0x604 <sum+10>:  mov    edx,DWORD PTR [rbp-0x4]
   0x607 <sum+13>:  mov    eax,DWORD PTR [rbp-0x8]
   0x60a <sum+16>:  add    eax,edx
   0x60c <sum+18>:  pop    rbp
   0x60d <sum+19>:  ret    
   0x60e <main>:    push   rbp
   0x60f <main+1>:  mov    rbp,rsp
   0x612 <main+4>:  sub    rsp,0x20
   0x616 <main+8>:  lea    rax,[rip+0xffffffffffffffdd]        # 0x5fa <sum>
   0x61d <main+15>: mov    QWORD PTR [rbp-0x18],rax
   0x621 <main+19>: lea    rax,[rip+0xffffffffffffffd2]        # 0x5fa <sum>
   0x628 <main+26>: mov    QWORD PTR [rbp-0x10],rax
   0x62c <main+30>: lea    rax,[rip+0xffffffffffffffc7]        # 0x5fa <sum>
   0x633 <main+37>: mov    QWORD PTR [rbp-0x8],rax
   0x637 <main+41>: mov    rax,QWORD PTR [rbp-0x18]
   0x63b <main+45>: mov    esi,0x4
   0x640 <main+50>: mov    edi,0x2
   0x645 <main+55>: call   rax
   0x647 <main+57>: mov    DWORD PTR [rbp-0x20],eax
   0x64a <main+60>: mov    esi,0x4
   0x64f <main+65>: mov    edi,0x2
   0x654 <main+70>: call   0x5fa <sum>
   0x659 <main+75>: mov    DWORD PTR [rbp-0x1c],eax
   0x65c <main+78>: mov    eax,0x0
   0x661 <main+83>: leave  
   0x662 <main+84>: ret  

我认为sum标签只是sum过程的起始地址-0x5fa,所以我不明白为什么gcc不能直接使用,而是使用计算@987654329 @为此。

问题:

  1. 为什么在汇编的lea rax, sum[rip]指令中使用sum[rip],而不是简单的sum标签,例如lea rax, sum?
  2. mov rax, 0x5fa 指令是否也会这样做?因为我们知道链接后的sum地址:call 0x5fa &lt;sum&gt;指令直接使用就行了。

【问题讨论】:

  • GDB 正在执行的程序指令: 不,它还没有执行。您从 GDB 内部反汇编了可执行文件,但地址是映像库的偏移量(直到进程开始才确定)。 start 命令之后,您会看到0x5555555546aa 之类的地址,用于main。此地址不是链接时间常数,因此它不能用作mov 的 32 位立即数。 (此外,它不适合 32 位,而是位置相关的可执行文件 do 中的静态地址,在 Linux 上。)
  • @PeterCordes 你说得对,我刚刚做了x /30i sum,没有start。并且想知道为什么所有地址都这么短,因为通常它们看起来像0x5555555545fa,正如你所说:)
  • @PeterCordes “此地址不是链接时间常数,因此不能用作 mov 的 32 位立即数。”但是为什么这条指令使用它作为常量:0x654 &lt;main+70&gt;: call 0x5fa &lt;sum&gt;?
  • near call/jmp 使用rel32 编码(felixcloutier.com/x86/CALL.html),两个静态地址之间的距离链接时间常数。 (或汇编时,用于来自同一源文件的位置)。 GDB 的反汇编程序会填写绝对地址,因为这样更有用,但是如果您查看 hexdump,您会看到相对编码。在 GDB 中使用disas /r,或者使用objdump -d

标签: c gcc assembly x86-64


【解决方案1】:

我相信这可能取决于您的 GCC 版本,但在我使用的 Linux 发行版上,所有内容都设置为默认为 PIC 版本。那是位置无关代码。这对共享库 可执行文件都更好,因为结果可以映射到任何地方的内存中,而无需修复传递。因为可以应用ASLR,所以安全性更好。

在 x86-64 中,使用 PIC 并没有明显的损失,那么为什么不在任何地方都使用它呢?

【讨论】:

  • 这里对 PIC 有一个惩罚:必须使用额外的 lea 指令,否则正常位移就足够了。不,直到最近,在 UNIX 上的 amd64 二进制文件中默认不使用 PIC。这是一个相当新的发展,称为 PIE。我对你的答案投了反对票,因为它实际上是不正确的,并且没有捕捉到引入 PIE 的实际动机。
  • 位置无关代码的惩罚很小但非零。访问非hidden 全局变量和无法内联函数的gcc -fPIC(使共享库安全代码尊重符号插入)的惩罚明显更高。无论如何,非 PIE 可执行文件不使用修正,它们具有由链接器选择的固定加载地址,因此mov edi, imm32 可以将静态地址放入寄存器。您可以使用[table + rcx*4] 对数组进行索引。见32-bit absolute addresses no longer allowed in x86-64 Linux?
猜你喜欢
  • 1970-01-01
  • 2021-04-12
  • 2016-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-08
  • 2017-11-02
  • 2023-03-04
相关资源
最近更新 更多