Значение записи в таблице перемещений объектного файла

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

//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 - это абсолютно одни и те же объекты. Почему у них разные названия символов?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
0
1 266
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это тот же адрес, но типы переселения разные. Типы перемещения определены в 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). Это также можно увидеть в результирующем двоичном файле - разные значения будут исправлены в заполнителях для этих двух разных типов перемещения.


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

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