Неожиданное значение локальной переменной указателя на функцию - PullRequest
2 голосов
/ 25 июня 2019

Я провел несколько экспериментов, в которых я создал локальную переменную типа указатель на функцию, которая указывает на printf.Затем я регулярно вызывал printf и использовал эту переменную следующим образом:

#include<stdio.h>
typedef int (*func)(const char*,...);

int main()
{
        func x=printf;
        printf("%p\n", x);
        x("%p\n", x);
        return 0;
}

Я скомпилировал ее, посмотрел на разборку main с помощью gdb и получил:

   0x000000000000063a <+0>:     push   %rbp
   0x000000000000063b <+1>:     mov    %rsp,%rbp
   0x000000000000063e <+4>:     sub    $0x10,%rsp
   0x0000000000000642 <+8>:     mov    0x20098f(%rip),%rax        # 0x200fd8
   0x0000000000000649 <+15>:    mov    %rax,-0x8(%rbp)
   0x000000000000064d <+19>:    mov    -0x8(%rbp),%rax
   0x0000000000000651 <+23>:    mov    %rax,%rsi
   0x0000000000000654 <+26>:    lea    0xb9(%rip),%rdi        # 0x714
   0x000000000000065b <+33>:    mov    $0x0,%eax
   0x0000000000000660 <+38>:    callq  0x520 <printf@plt>
   0x0000000000000665 <+43>:    mov    -0x8(%rbp),%rax
   0x0000000000000669 <+47>:    mov    -0x8(%rbp),%rdx
   0x000000000000066d <+51>:    mov    %rax,%rsi
   0x0000000000000670 <+54>:    lea    0x9d(%rip),%rdi        # 0x714
   0x0000000000000677 <+61>:    mov    $0x0,%eax
   0x000000000000067c <+66>:    callq  *%rdx
   0x000000000000067e <+68>:    mov    $0x0,%eax
   0x0000000000000683 <+73>:    leaveq
   0x0000000000000684 <+74>:    retq

Чтодля меня странно, что вызов printf напрямую использует plt (как и ожидалось) , но вызов его с использованием локальной переменной использует совершенно другой адрес (как вы можете видеть в строке 4 сборки, чтозначение, хранящееся в локальной переменной x, не является адресом записи plt).

Как это может быть?Разве все вызовы функций, не определенных в исполняемом файле, не проходят сначала через plt для повышения производительности и для кода pic?

Ответы [ 2 ]

2 голосов
/ 25 июня 2019

Четвертая и пятая строки вашей разборки соответствуют утверждению func x=printf; в вашем коде.Адрес printf хранится в памяти по адресу 0x200fd8, доступ к которому осуществляется по относительному адресу rip (0x20098f(%rip)).Затем он сохраняется в локальной переменной (относительно ebp, по адресу -0x8(%rbp)).

Любые корректировки, необходимые во время выполнения, будут выполнены со значением, сохраненным в 0x200fd8.

0 голосов
/ 25 июня 2019

Функция имеет один адрес для всей программы, но для каждой разделяемой библиотеки существует PLT, в результате чего различные указатели на printf будут иметь разные значения.

...