смысл записи в таблице перемещений объектного файла - PullRequest
0 голосов
/ 07 сентября 2018

Я столкнулся с некоторыми проблемами в понимании записей таблиц перемещения, скомпилированных из исходных файлов на языке Си. Мои программы, как показано ниже:

//a.c
extern int shared;
int main(){
    int a = 100;
    swap(&a, &shared);
    a = 200;
    shared = 1;
    swap(&a, &shared);
}
//b.c
int shared = 1;
void swap(int* a, int* b) {
    if (a != b)
        *b ^= *a ^= *b, *a ^= *b;
}

Я компилирую и связываю их с помощью следующих команд gcc -c -fno-stack-protector a.c b.c и ld a.o b.o -e main -o ab. Тогда я objdump -r a.o проверил свою таблицу перемещений.

RELOCATION RECORDS FOR [.text]:
OFFSET           TYPE              VALUE 
0000000000000014 R_X86_64_32       shared
0000000000000021 R_X86_64_PC32     swap-0x0000000000000004
000000000000002e R_X86_64_PC32     shared-0x0000000000000008
000000000000003b R_X86_64_32       shared
0000000000000048 R_X86_64_PC32     swap-0x0000000000000004

Разборка a.o - это

Disassembly of section .text:

0000000000000000 <main>:
0:  55                      push   %rbp
1:  48 89 e5                mov    %rsp,%rbp
4:  48 83 ec 10             sub    $0x10,%rsp
8:  c7 45 fc 64 00 00 00    movl   $0x64,-0x4(%rbp)
f:  48 8d 45 fc             lea    -0x4(%rbp),%rax
13: be 00 00 00 00          mov    $0x0,%esi
18: 48 89 c7                mov    %rax,%rdi
1b: b8 00 00 00 00          mov    $0x0,%eax
20: e8 00 00 00 00          callq  25 <main+0x25>
25: c7 45 fc c8 00 00 00    movl   $0xc8,-0x4(%rbp)
2c: c7 05 00 00 00 00 01    movl   $0x1,0x0(%rip)  # 36 <main+0x36>
33: 00 00 00 
36: 48 8d 45 fc             lea    -0x4(%rbp),%rax
3a: be 00 00 00 00          mov    $0x0,%esi
3f: 48 89 c7                mov    %rax,%rdi
42: b8 00 00 00 00          mov    $0x0,%eax
47: e8 00 00 00 00          callq  4c <main+0x4c>
4c: b8 00 00 00 00          mov    $0x0,%eax
51: c9                      leaveq 
52: c3                      retq  

Мой вопрос таков: shared в 14 и shared в 2e - абсолютно одинаковые объекты. Почему у них разные имена символов?

1 Ответ

0 голосов
/ 11 сентября 2018

Это тот же адрес, но типы перемещения разные.Типы перемещения определены в x86-64-abi .

В чем разница?

В 0x14 и 0x3b: адрес глобальной переменнойshared необходимо переместить в регистр %rsi, чтобы вызвать функцию swap.

Однако, поскольку программа была скомпилирована с -mcmodel=small (по умолчанию для gcc, см. Также этот вопрос), компилятор может предположить, что адрес соответствует 32-битному и использует movl вместо movq (на самом деле компилятор будет использовать другие инструкции в противном случае, но сравнение movl с «наивным» movq объясняетразница очень хорошая), что потребовало бы большего количества байтов для кодирования.

Таким образом, результирующее перемещение будет R_X86_64_32 (т.е. 64-битный адрес урезан до 32-битного без расширения знака), а не R_X86_64_64, т.е. компоновщикзапишет 4 младших байта адреса вместо заполнителя, который также имеет ширину 4 байта.

При 0x2e вы хотите записать значение 1 в адрес памяти shared.Однако целевой адрес задается относительно %rip, т. Е. Относительно 0x36:

movl   $0x1,0x0(%rip)  # 36 <main+0x36>

Очевидно, что простое введение абсолютного адреса shared через R_X86_64_32 не сработаетхорошо - требуется более сложное вычисление, и именно для этого R_X86_64_PC32.

Еще раз, из-за небольшой модели кода, компилятор может предположить, что 32-битного относительного смещения рипа достаточно (итаким образом, используется перемещение R_X86_64_PC32, а не R_X86_64_PC64), а заполнитель имеет ширину всего 4 байта.

Взято из x86-64-abi, формула для перемещения: (раздел 4.4):

result = S+A-P (32bit-word, i.e. the lower 4 bytes of the result) 
S = the value of the symbol whose index resides in the relocation entry 
A = the addend used to compute the value of the relocatable field 
P = the place (section offset or address) of the storage unit being relocated (computed using r_offset)

Это означает:

  • S - адрес переменной shared.
  • A равно -8 (это можно увидеть, например, вызвав readelf -r a.o или objdump -r a.o), потому что существует разница в 8 байт между смещением перемещения 0x2e и фактическим %rip - 0x36.
  • P - это смещение перемещения, т.е. 0x26.P-A - это адрес в %rip.

Как видите, результат не S, как в случае R_X86_64_32 выше, а S - (P-A).Это также можно увидеть в полученном двоичном файле - в заполнителях для этих двух разных типов перемещения будут исправлены разные значения.


Там есть отличная статья на эту тему от EliБендерский.

...