Почему полученное смещение записи отображается неправильно?
Я написал простую общую библиотеку:
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 перейдет прямо к нужному коду.