Почему полученное смещение записи отображается неправильно?

Я написал простую общую библиотеку:

extern void some_func(void);

void
function(void)
{
        some_func();
}

Составитель / постройки:

gcc -fPIC -mcmodel=large -c test.c -o test.o
gcc -fPIC -shared test.o -o libtest.so

Разобрать, чтобы увидеть, как ссылаются на some_func:

$ objdump -d libtest.so
00000000000006a0 <function>:
 6a0:   55                      push   %rbp
 6a1:   48 89 e5                mov    %rsp,%rbp
 6a4:   41 57                   push   %r15
 6a6:   48 83 ec 08             sub    $0x8,%rsp
 6aa:   48 8d 05 f9 ff ff ff    lea    -0x7(%rip),%rax        # 6aa <function+0xa>
 6b1:   49 bb 56 09 20 00 00    movabs $0x200956,%r11
 6b8:   00 00 00 
 6bb:   4c 01 d8                add    %r11,%rax
 6be:   49 89 c7                mov    %rax,%r15
 6c1:   48 ba 80 f5 df ff ff    movabs $0xffffffffffdff580,%rdx
 6c8:   ff ff ff 
 6cb:   48 01 c2                add    %rax,%rdx
 6ce:   ff d2                   callq  *%rdx
 6d0:   90                      nop
 6d1:   48 83 c4 08             add    $0x8,%rsp
 6d5:   41 5f                   pop    %r15
 6d7:   5d                      pop    %rbp
 6d8:   c3                      retq

Посмотрел где .got.plt расположен:

$ readelf -S libtest.so
...
[21] .got.plt          PROGBITS         0000000000201000  00001000
       0000000000000020  0000000000000008  WA       0     0     8
...

Что такое переезд:

$ readelf -r libtest.so
Relocation section '.rela.plt' at offset 0x538 contains 1 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000201018  000400000007 R_X86_64_JUMP_SLO 0000000000000000 some_func + 0


В 6aa-6bb мы получаем абсолютное местоположение GOT: 6aa + 0x200956 = 0x201000, что согласуется с readelf -S libtest.so выходной.

Мы пропускаем 3 зарезервированных байта в GOT(связанных с функциями) и определяем, что абсолютный адрес some_func должен быть найден со смещением +0x18(четвертый байт из GOT) во время выполнения.

Согласен с readelf -r libtest.so,

Но инструкция 6c1 по разборке objdump показывает:

  movabs $0xfff...dff580, %rdx  

Я ожидаю, что исходный операнд будет держать +0x18 (смещение от GOT, его адрес находится по адресу rax), но вместо этого оно имеет большое отрицательное число.

Не могли бы вы объяснить, что показывает этот номер, но не 0x18?

1 ответ

Решение

Существует два вида перемещений: статическое и динамическое (1); один для статического компоновщика ld и другое для загрузчика (динамический компоновщик, RTLD) - ld-linux.so.2 для linux glibc 2.* (см. " Динамическое связывание и загрузка", 1999 или " Статические линкеры и загрузчики Dyanmic").

Когда вы используете objdump чтобы сбросить перемещения, он имеет -r опция для статических перемещений, и -R для динамических перемещений.

Ваш случай - это не просто GOT, это GOT.PLT - GOT, используемый для процедурной связи. Этот вид доступа использует динамическое перемещение. Итак, вы должны проверить вывод objdump -dR libtest.so, он покажет вам как разборки, так и динамические перемещения в нем.

Цитированная строка из readelf -r libtest.so только для таблицы PLT, а не для кода.

http://www.airs.com/blog/archives/41

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

jmp *offset(%ebx)
pushl #index
jmp first_plt_entry

Компоновщик программы выделит запись в GOT для каждой записи в PLT. Это создаст динамическое перемещение для записи GOT типа JMP_SLOT. Он инициализирует запись GOT по базовому адресу совместно используемой библиотеки плюс адрес второй инструкции в кодовой последовательности выше. Когда динамический компоновщик выполняет начальную отложенную привязку для перемещения JMP_SLOT, он просто добавляет разницу между адресом загрузки общей библиотеки и базовым адресом общей библиотеки в запись GOT. В результате первая команда jmp перейдет ко второй инструкции, которая подтолкнет запись индекса и перейдет к первой записи PLT. Первая запись PLT является особенной и выглядит следующим образом:

pushl 4(%ebx)
jmp *8(%ebx)

Это относится ко второй и третьей записи в GOT. Динамический компоновщик будет инициализировать их, чтобы иметь соответствующие значения для обратного вызова в сам динамический компоновщик. Динамический компоновщик будет использовать индекс, выдвинутый первой кодовой последовательностью, чтобы найти перемещение JMP_SLOT. Когда динамический компоновщик определяет вызываемую функцию, он сохраняет адрес функции в ссылках GOT-записи по первой последовательности кода. Таким образом, при следующем вызове функции инструкция jmp перейдет прямо к нужному коду.

Другие вопросы по тегам