Разница между основанием и смещением - PullRequest
0 голосов
/ 29 сентября 2018

У меня есть некоторые проблемы с пониманием двух инструкций, с которыми я столкнулся.
Первая следующая:

imul   eax,DWORD PTR [esi+ebx*4-0x4]

Означает ли эта инструкция => Возьмите значение по адресу, который вы рассчитываете в скобках, умножьте егопо eax и сохранить его в том же регистре (eax)?Если это так, мы рассчитываем адрес в скобках, как это?

  1. ebx * 4
  2. esi + результат операции 1
  3. вычесть 4 из результата
  4. перейти по адресу (результат) иполучите значение внутри него.

Вторая инструкция, которую я имею для декодирования проблемы, это

jmp    DWORD PTR [eax*4+0x80497e8]

-Экс * 4 эквивалентно индексу * scale?
-Is0x80497e8 смещение?

Итак, чтобы получить адрес в скобках, это тот порядок, которому мы должны следовать?

  1. eax * 4
  2. добавить результат в 1. к адресу 0x80497e8
  3. перейти к этому адресу

В моем понимании[base + index * scale] используется для извлечения значений внутри и массива.Основой является указатель на первый элемент в массиве.Индекс - это буквально индекс, в котором хранится значение, которое мы хотим. А масштаб - это размер даты, содержащейся в массиве

Моя проблема в том, когда вы добавляете смещение в уравнение, для чего используется смещение?И что это значит, когда смещение имеет отрицательное значение?

1 Ответ

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

Не обманывайтесь терминологией.«base» имеет конкретное техническое значение, а компонент «base» режима адресации не имеет для начала массива.Например, [esp + 16 + esi*4] может индексировать локальный массив, который начинается с esp+16, даже если esp является базовым регистром.

Аналогично, наиболее очевидная интерпретация [esi+ebx*4-0x4] это array[i-1], с i в EBX и esi, содержащий начальный адрес массива.Это очевидная оптимизация для компилятора, чтобы сложить -1 в режим адресации вместо вычисления ebx-1 в другом регистре и использовать его в качестве индекса.

И что это означает, когда смещениеимеет отрицательное значение?

Это ничего не значит.Аппаратное обеспечение просто выполняет двоичное сложение и использует результат.Программист (или компилятор) должен использовать режим адресации, который обращается к нужному байту.

Мой ответ на Ссылка на содержимое ячейки памяти.(режимы адресации x86) содержит примеры случаев, когда вы можете использовать любой возможный режим адресации для индексации массива, либо с указателем на массив, либо со статическим массивом (так что вы можете жестко закодировать начальный адрес массива как абсолютное смещение).


В технической терминологии режима адресации x86:

  • смещение: + - постоянная часть адреса , закодированная в виде знака дополнения до 2-расширенный disp8 или disp32.(В 64-битных режимах адресации disp32 расширяется до 64 бит).
  • смещение: результат вычисления esi+ebx*4-0x4: смещение относительно сегментабаза .(В обычной плоской модели памяти с base = 0, смещение = весь адрес).

    Люди часто используют «смещение» для описания смещения, и обычно не возникает путаницы, поскольку из контекста ясно, что ониговорить о постоянном смещении (используя английское слово offset в каком-то смысле, отличном от x86 seg:off), но мне нравится придерживаться «смещения», чтобы описать смещение.

  • base: неиндексный регистровый компонент режима адресации, если он есть .(Вместо этого кодировка «без базового регистра» означает, что есть disp32, и вы можете думать об этом как о базе. Это подразумевает сегмент DS.)

    Это включает в себя случай, когда имеется только индекс инет базового регистра: [esi*4] можно кодировать только как [dword 0 + esi*4].


imul   eax,DWORD PTR [esi+ebx*4-0x4]

Да, eax *= memory source operand.

И да, вашРасчет адреса правильный.Base + масштабированный индекс + смещение со знаком, в результате чего виртуальный адрес 1 .

«перейти к адресу (результат) и получить значение внутри него» - это странный способ описать его.«Перейти к» обычно означает передачу управления, выбирая байты как код.Но это не то, что происходит, это всего лишь загрузка данных с этого адреса, полностью обрабатываемая аппаратными средствами.

Современный процессор x86 (например, Intel Skylake) декодирует imul eax, [esi+ebx*4 - 4] в два мопа: imul ALUэксплуатация и нагрузка.Операция ALU должна ждать результата загрузки.(Забавный факт: две микрооперации фактически объединены в одну моп для большей части конвейера, за исключением планировщика вне очереди. Подробнее см. https://agner.org/optimize/.)

Когда работает load uop, блок генерирования адреса (AGU) получает 2 входных регистра, масштабный коэффициент индекса (смещение влево на 2) и немедленное смещение (-4).Аппаратура сдвига и добавления в AGU вычисляет адрес загрузки.

Следующим шагом в модуле выполнения загрузки является использование этого адреса для загрузки из кэша L1d (который имеет виртуальный L1dTLB первого уровня->физический кеш в основном встроен. L1d практически индексируется, поэтому поиск в TLB может происходить параллельно с извлечением набора из 8 тегов + данных из этого способа из кеша L1d).Предполагая попадание в кэш L1dTLB и L1d, модуль выполнения загрузки получает результат загрузки ~ 5 циклов спустя.

Этот результат загрузки передается исполнительному блоку ALU в качестве исходного операнда.ALU не волнует, был ли это imul eax, ebx или операнд источника памяти;этот коэффициент умножения просто отправляется в АЛУ, как только оба входных операнда готовы.


jmp    DWORD PTR [eax*4+0x80497e8]

Да, eax *4 - это масштабированный индекс.

Да, 0x80497e8 - смещение disp32.В этом случае компонент смещения режима адресации, вероятно, используется в качестве адреса статической таблицы переходов. Вы можете рассматривать его как основу для этого режима адресации.

перейти по этому адресу

Нет, загрузить новое значение EIP с этого адреса .Это скачок памяти из-за квадратных скобок.

То, что вы описали, будет

lea   eax, [eax*4+0x80497e8]       ; address calc
jmp   eax                          ; jump to code at that address

Невозможно выполнить вычисленный переход в одной инструкции, вам всегда нужен новый EIPзначение должно быть в регистре или извлечено как данные из памяти.


Сноска 1: Мы предполагаем модель с плоской памятью (основание сегмента = 0), поэтому мы можем игнорировать сегментацию, как обычно для кодаработает под нормальной ОС, такой как Linux, Windows, OS X, или почти любой 32- или 64-разрядной ОС.Таким образом, вычисление адреса дает вам линейный адрес.

Я также предполагаю, что подкачка включена, как обычно в основной ОС, так что это виртуальный адрес, который должен быть переведен в физический с помощью таблиц страниц.кэшируется TLB.

...