Как переменные в разделяемых библиотеках ссылаются на загрузчик? - PullRequest
2 голосов
/ 10 июня 2011

Теперь я понимаю, как на динамические функции ссылаются таблицы процедур, как показано ниже:

Dump of assembler code for function foo@plt:
0x0000000000400528 <foo@plt+0>: jmpq   *0x2004d2(%rip)        # 0x600a00 <_GLOBAL_OFFSET_TABLE_+40>
0x000000000040052e <foo@plt+6>: pushq  $0x2
0x0000000000400533 <foo@plt+11>:    jmpq   0x4004f8
(gdb) disas 0x4004f8
No function contains specified address.

Но я не знаю, как на динамические переменные ссылаются, хотя я обнаружил, что значения заполняются в GOT после запуска, но нет заглушки, как указано выше, как она работает?

Ответы [ 2 ]

1 голос
/ 28 июня 2011

Динамический загрузчик перемещает все ссылки на переменные перед передачей управления пользовательской программе.

Для них нет «заглушки», потому что как только пользовательская программа начинает выполняться, загрузчик не может восстановить управление и обновить адреса переменных. Если вам это не ясно, значит, вы действительно не поняли, как работает заглушка с отложенным разрешением PLT.

0 голосов
/ 05 августа 2011

Глобальные переменные доступны косвенно, через глобальную таблицу смещений.

  • При компиляции программы компилятор генерирует код, который выполняет косвенный доступ и испускает информацию о перемещении с указанием используемая запись в глобальной таблице смещений.
  • Компоновщик выполняет эти перемещения при создании финала динамически загружаемый объект, в результате чего получается машинный код, который не необходимо дальнейшее исправление во время загрузки.

Чтобы увидеть это в действии, рассмотрим следующий фрагмент кода.

int v1;
int f(void) { return !v1; }

Функция f ссылается на глобальный v1. Сгенерированный машинный код для функции выглядит следующим образом (на i386):

% gcc -c -fpic a.c
% objdump --disassemble --reloc a.o
[snip]
Disassembly of section .text:

00000000 <f>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   e8 fc ff ff ff          call   4 <f+0x4>
            4: R_386_PC32   __i686.get_pc_thunk.cx
   8:   81 c1 02 00 00 00       add    $0x2,%ecx
            a: R_386_GOTPC  _GLOBAL_OFFSET_TABLE_
   e:   8b 81 00 00 00 00       mov    0x0(%ecx),%eax
            10: R_386_GOT32 v1
  14:   8b 00                   mov    (%eax),%eax
  16:   85 c0                   test   %eax,%eax
  18:   0f 94 c0                sete   %al
  1b:   0f b6 c0                movzbl %al,%eax
  1e:   5d                      pop    %ebp
  1f:   c3                      ret    

Disassembly of section .text.__i686.get_pc_thunk.cx:

00000000 <__i686.get_pc_thunk.cx>:
   0:   8b 0c 24                mov    (%esp),%ecx
   3:   c3                      ret

Проход по машинному коду:

  • (смещения 0x0 и 0x1) Стандартный пролог функции.
  • (смещение 0x3) Звонок на __i686.get_pc_thunk.cx готовится к Относительная к ПК адресация путем загрузки адреса инструкции после звонка в регистр %ecx.
  • (смещение 0x8) Значение в %ecx настроено так, чтобы указывать на начало глобальной таблицы смещений. Эта корректировка сигнализируется запись о перемещении типа R_386_GOTPC.
  • (смещение 0xE) Получен глобальный адрес v1. Перемещение R_386_GOT32 обеспечивает смещение записи v1 от база таблицы глобальных смещений.
  • (смещение 0x14) Значение в v1 извлекается в регистр %eax.
  • (смещения 0x16--0x1F) Остальные вычисления для функции f.

В конечном общем объекте компоновщик исправляет код функции следующее:

% gcc -shared -o a.so a.o
% objdump --disassemble a.so
...snip...
0000044c <f>:
 44c:   55                      push   %ebp
 44d:   89 e5                   mov    %esp,%ebp
 44f:   e8 18 00 00 00          call   46c <__i686.get_pc_thunk.cx>
 454:   81 c1 a0 1b 00 00       add    $0x1ba0,%ecx
 45a:   8b 81 f8 ff ff ff       mov    -0x8(%ecx),%eax
 460:   8b 00                   mov    (%eax),%eax
 462:   85 c0                   test   %eax,%eax
...snip...
  • Если предположить, что объект загружается со смещением O в память, инструкция вызова со смещением 0x44F будет загружать O + 0x454 + 0x1BA0, т.е. O + 0x1FF4 в %ecx.
  • Инструкция со смещением 0x45A вычитает 8 из %ecx чтобы получить адрес слота для v1 в глобальной таблице смещений, то есть слот для v1 находится по смещению 0x1FEC от начала общий объект.

Рассматривая записи динамического перемещения для общего объекта, мы увидеть запись о перемещении, дающую указание загрузчику времени выполнения сохранить фактический адрес для v1 со смещением 0x1FEC.

% objdump -R a.so
DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE
...snip...
00001fec R_386_GLOB_DAT    v1
...snip...

Дальнейшее чтение:

...