LINUX.ORG.RU

Поясните про смысл lea с %rip при доступе к переменной

 ,


0

1

По мотивам Меняю const. Стреляю себе в ногу и получается (комментарий)

Смысл в том, что если есть программа, а в ней объявлена переменная, например a0, как выше по ссылке (привожу без const для корректности)

#include <stdio.h>

static  unsigned a0 = 0;
int main() {
	*((int *)&a0) = 102; //или без выкрутасов просто a0=102
	
	printf("%u\n", a0);
	
	return 0;
}

То в ассемблерном листинге в месте, где присваивание, будет что-то вроде

leaq	a0(%rip), %rax
movl	$102, (%rax)

И вот я этого синтаксиса для lea понять не могу. То есть, ясно, что в rax грузится адрес переменной a0, чтобы в следующей инструкции туда по этому адресу mov числа 102 устроить, но что там делает rip - я этой магии в упор не понимаю.

Я это вижу как что буквально в rax грузится адрес a0+rip, но это же абсурд в данном случае (если кто скажет, что та программа не корректна - это не так, в корректной тоже rip)

Поясните пожауйста, что не так.

★★★★★

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

slovazap ★★★★★
()
symbol(%rip) - at&t
[rip + symbol] - intel

это спец синтаксис для относительно-IP адресации. Адрес symbol берется относительно текущей, точнее следующей, выполняемой инструкции.

anonymous
()
Ответ на: комментарий от slovazap

Хм, там в любом случае 64-битные регистры используются, так что экономии байт не видно.

Ну и тогда получается, что адрес a0 указывается как

[a0] = [истинный a0]-rip

- да если расстояние между секциями небольшими это способно уменьшить число, но смысл.

praseodim ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Адрес symbol берется относительно текущей, точнее следующей, выполняемой инструкции.

Тогда было бы rip - symbol, а не плюс. То есть, выходит что из истинного адреса вычитается текущий rip, чтобы его потом прибавить. Но нафейхоа?!

praseodim ★★★★★
() автор топика

вот тут есть пояснение

https://stackoverflow.com/a/58108641

Из-за того что нет причин «почему нет», компилятор предпочитает 32битную адресацию.

Главная ошибка, я думаю, в ходе мысли

Тогда было бы rip - symbol, а не плюс

Кто сказал что symbol это число положительное?

anonymous
()
Ответ на: комментарий от praseodim

Тогда было бы rip - symbol, а не плюс. То есть, выходит что из истинного адреса вычитается текущий rip, чтобы его потом прибавить. Но нафейхоа?!

Это адресация относительно адреса след. инструкции. Потому что по какому адресу реально загрузит это дело линкер неизвестно. А относительно %rip в рамках секции всё ок.

anonymous
()
Ответ на: комментарий от praseodim

Тогда было бы rip - symbol, а не плюс

Ты понимаешь термин «спец. синтаксис»? Подсказка: это не бинарный код. Можешь посмотреть, во что это компилируется, там чуть другая арифметика, использующая адрес следующей инструкции, вроде

И да, gcc -no-pie, если хочется развидеть это. Вроде.

anonymous
()

Насколько я знаю, в AMD64 нельзя просто так взять и обратиться по 64-битовому смещению:

    mov    eax, [absolue_64_bit_address]

Точнее написать-то ты такую команду можешь, но ассемблер автоматически переведет ее в адресацию относительно RIP. Что ты и видишь в листинге. Если ты посмотришь, как эта же последовательность байт дизассемблируется другими дизассемблерами (IDA, HIEW), то скорее всего, никакого RIP ты в команде не найдешь - они автоматически преобразуют ее в абсолютное смещение.

Так что все только через мумба-юмбу.

Ну а смысл - например, релоки не нужны.

bigbit ★★★★★
()
Последнее исправление: bigbit (всего исправлений: 2)
Ответ на: комментарий от praseodim

Хм, там в любом случае 64-битные регистры используются, так что экономии байт не видно

Почему ты думаешь что в коде оно будет занимать 8 байт? Наверняка у lea есть варианты кодирования где на смещение тратится 1, 2 или 4 байта. Я не смог получить код с lea на compiler explorer, но смог получить такое:

b8 34 40 40 00
mov    eax,0x404034
c7 00 66 00 00 00
mov    DWORD PTR [rax],0x66

Тут похоже на абсолютное смещение, но даже так используется 4 байта, и это даёт экономию на этой инструкции почти в половину.

slovazap ★★★★★
()

Это position-independent code. Код будет работать одинаково вне зависимости от того, где его решит разместить линкер. Раньше для этого использовали GOT-указатель, сейчас чаще адресуются относительно IP.

Miguel ★★★★★
()
Ответ на: комментарий от praseodim

Напишу подробнее, т.к. сам только что поисследовал это.

В IA32 инструкция «mov eax, [012345678]» будет представлена байтами A1 78 56 34 12. Последние 4 байта - смещение относительно нуля.

A178563412                     mov         eax, [012345678]

В AMD64 та же самая инструкция «mov eax, [012345678]» будет представлена как 8B 05 72 56 34 12. Последние 4 байта - смещение относительно RIP следующей команды (если RIP текущей команды = 0). А аналогичной формы команды с абсолютным адресом как в IA32, похоже, в 64-битном режиме нет в принципе.

8B0572563412                   mov         eax, [012345678]

Т.е. в AMD64 одна и та же инструкция «mov eax, [012345678]» будет представлена различными байтами, в зависимости от того, по какому смещению RIP она располагается.

bigbit ★★★★★
()
Последнее исправление: bigbit (всего исправлений: 1)
Ответ на: комментарий от praseodim

https://sourceware.org/binutils/docs/as/i386_002dMemory.html

Там в конце страницы есть объяснение того, что происходит. symbol(%rip) это такой особый случай. В машинный код попадёт не адрес symbol, а смещение symbol относительно адреса следующей инструкции. Так код получается проще.

А иначе придётся тебе ставить метку на следующую инструкцию и писать что-то типа (symbol - next_inst_addr)(%rip). Лишняя боль. Плюс ещё будут проблемы с релокациями, потому что не факт, что константа будет в секции .text, и тогда даже разницу symbol - next_inst_addr нельзя будет вычислить до финальной компоновки.

i-rinat ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.