Значение R_386_32/R_386_PC32 в разделе .rel.text эльфа

Чтобы понять концепцию перемещения, я написал простую программу chk.c следующим образом:

  1 #include<stdio.h>
  2 main(){
  3         int x,y,sum;
  4         x = 3;
  5         y = 4;
  6         sum = x + y;
  7         printf("sum = %d\n",sum);
  8 }

его эквивалентный код сборки, используя "objdump -d chk.o":

00000000 <main>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 e4 f0                and    $0xfffffff0,%esp
   6:   83 ec 20                sub    $0x20,%esp
   9:   c7 44 24 1c 03 00 00    movl   $0x3,0x1c(%esp)
  10:   00 
  11:   c7 44 24 18 04 00 00    movl   $0x4,0x18(%esp)
  18:   00 
  19:   8b 44 24 18             mov    0x18(%esp),%eax
  1d:   8b 54 24 1c             mov    0x1c(%esp),%edx
  21:   8d 04 02                lea    (%edx,%eax,1),%eax
  24:   89 44 24 14             mov    %eax,0x14(%esp)
  28:   b8 00 00 00 00          mov    $0x0,%eax
  2d:   8b 54 24 14             mov    0x14(%esp),%edx
  31:   89 54 24 04             mov    %edx,0x4(%esp)
  35:   89 04 24                mov    %eax,(%esp)
  38:   e8 fc ff ff ff          call   39 <main+0x39>
  3d:   c9                      leave  
  3e:   c3                      ret    
Раздел

и .rel.text, видимый с использованием readelf, выглядит следующим образом:

Relocation section '.rel.text' at offset 0x360 contains 2 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
00000029  00000501 R_386_32          00000000   .rodata
00000039  00000902 R_386_PC32        00000000   printf

У меня есть следующие вопросы, основанные на этом:

1) из второй записи в разделе .rel.text, я могу понять, что значение в смещении 0x39 в .text-разделе (которое здесь равно 0xfcffffff) должно быть заменено адресом символа, связанного с индексом 9 символа table (&, который выходит printf). Но я не могу четко понять смысл 0x02 (ELF32_R_TYPE) здесь. Что здесь задает R_386_PC32? Может кто-нибудь объяснить его смысл ясно.

2) Я также не могу понять первую запись. что нужно заменить при смещении 0x29 в разделе .text и почему здесь неясно. Снова я хочу знать значение R_386_32 здесь. Я нашел один файл pdf elf_format.pdf, но я не могу четко понять значение "Тип" в разделе .rel.text.

3) Также я хочу знать смысл сборки inst "lea (% edx,% eax, 1),% eax". Хотя я нашел очень хорошую ссылку (Какова цель инструкции LEA?), описывающая значение lea, но формат lea (то есть 3 arg внутри скобок) неясно.

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

еще один вопрос. я показал записи таблицы символов для обоих смещений 5 и 9. ниже.

 Num: Value Size Type Bind Vis Ndx Name 
 5: 00000000 0 SECTION LOCAL DEFAULT 5 
 9: 00000000 0 NOTYPE GLOBAL DEFAULT UND printf' 

Информационное поле для первой записи в таблице .rel.text равно 0x05, которая указывает индекс таблицы символов. Я показал запись таблицы символов для индекса 5 выше, но не могу понять, как это говорит нам о том, что это для .rodata.

Ответ 1

1), 2): R_386_32 - это перемещение, которое помещает абсолютный 32-разрядный адрес символа в указанное место памяти. R_386_PC32 - это перемещение, которое помещает 32-битный адрес символа PC в указанный адрес памяти. R_386_32 полезен для статических данных, как показано здесь, поскольку компилятор просто загружает перемещенный адрес символа в некоторый регистр, а затем обрабатывает его как указатель. R_386_PC32 полезен для ссылок на функции, поскольку он может использоваться как непосредственный аргумент для call. См. elf_machdep.c для примера того, как обрабатываются репозиции.

3) lea (%edx,%eax,1),%eax означает просто %eax = %edx + 1*%eax, если он выражен в синтаксисе Си. Здесь он в основном используется в качестве замены кода операции add.

EDIT: Здесь пример.

Предположим, что ваш код загружается в память, начиная с 0x401000, строка "sum = %d\n" заканчивается на 0x401800 (в начале раздела .rodata) и что printf находится в 0x1400ab80, в libc.

Затем перенос R_386_32 на 0x29 поместит байты 00 18 40 00 на 0x401029 (просто копируя абсолютный адрес символа), делая команду на 0x401028

  401028:   b8 00 18 40 00          mov    $0x401800,%eax

Перемещение R_386_PC32 в 0x39 помещает байты 43 9b c0 13 в 0x401039 (значение 0x1400ab80 - 0x40103d = 0x13c09b43 в шестнадцатеричном виде), делая эту инструкцию

  401038:   e8 43 9b c0 13          call   $0x1400ab80 <printf>

Мы вычитаем 0x40103d для учета значения% pc (который является адресом команды после call).

Ответ 2

Первая запись перемещения - это получить указатель на строку формата ("sum = ...") в процессе настройки вызова printf. Поскольку раздел .rodata перемещается, а также раздел .text, ссылки на строки и другие постоянные данные нуждаются в исправлениях.

С учетом этого, похоже, что перераспределение R_386_32 связано с данными, а R_386_PC32 с кодовыми адресами, но спецификация ELF (у которой у меня нет удобной копии), вероятно, объясняет различные детали.

Инструкция lea - это то, что компилятор решил выполнить дополнение для этой процедуры. Возможно, он выбрал add или пару других возможностей, но эта форма lea, по-видимому, используется довольно часто для определенных случаев, поскольку она может комбинировать добавление с умножением. Результат команды lea (%edx,%eax,1),%eax заключается в том, что %eax получит значение %edx + 1 * %eax. 1 можно заменить ограниченным набором малых целых чисел. Первоначальной целью команды lea был "Load Effective Address" - взять базовый указатель, индекс и размер и присвоить адрес элемента в массиве. Но, как вы можете видеть, компиляторы могут использовать его и для других вещей...