Не обманывайтесь терминологией.«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.