Как относительный JMP (x86) реализован в Ассемблере? - PullRequest
8 голосов
/ 12 мая 2010

При сборке моего ассемблера для платформы x86 я столкнулся с некоторыми проблемами при кодировании инструкции JMP:

OPCODE   INSTRUCTION   SIZE
 EB cb     JMP rel8     2
 E9 cw     JMP rel16    4 (because of 0x66 16-bit prefix)
 E9 cd     JMP rel32    5
 ...

( с моего любимого сайта инструкций x86, http://siyobik.info/index.php?module=x86&id=147)

Все это относительные переходы, где размер каждой кодировки (операция + операнд) находится в третьем столбце.

Теперь мой оригинальный (и, следовательно, неисправный из-за этого) дизайн зарезервировал максимальное (5 байт) пространство для каждой инструкции. Операнд еще не известен, потому что это прыжок в неизвестное место. Поэтому я реализовал механизм «перезаписи», который переписывает операнды в правильном месте в памяти, если местоположение перехода известно, и заполняет остальное NOP с. Это несколько серьезная проблема в петлях.

Теперь моя проблема в следующей ситуации:

b: XXX
c: JMP a
e: XXX
   ...
   XXX
d: JMP b
a: XXX      (where XXX is any instruction, depending
             on the to-be assembled program)

Проблема в том, что я хочу наименьшее возможное кодирование для инструкции JMP (без заполнения NOP).

Мне нужно знать размер инструкции в c, прежде чем я смогу вычислить относительное расстояние между a и b для операнда в d. То же самое относится к JMP в c: ему нужно знать размер d, прежде чем он сможет рассчитать относительное расстояние между e и a.

Как существующие ассемблеры решают эту проблему или как вы это сделаете?

Вот что я думаю, что решает проблему:

Сначала закодируйте все инструкции для кодов операций между JMP и его целью, если этот регион содержит код операции переменного размера, используйте максимальный размер, например, 5 для JMP. Затем закодируйте относительный JMP к своей цели, выбрав наименьший возможный размер кодировки (3, 4 или 5) и рассчитайте расстояние. Если какой-либо код операции переменного размера закодирован, измените все абсолютные операнды и все относительные инструкции, которые пропускают эту закодированную инструкцию: они перекодируются, когда их операнд изменяется, чтобы выбрать наименьший возможный размер. Этот метод гарантированно завершится, поскольку только коды операций переменного размера могут уменьшаться (поскольку он использует их максимальный размер).

Интересно, возможно, это слишком спроектированное решение , поэтому я задаю этот вопрос.

Ответы [ 2 ]

3 голосов
/ 12 мая 2010

В первом проходе у вас будет очень хорошее приближение, к которому jmp код будет использоваться, используя пессимистический подсчет байтов для всех инструкций перехода.

На втором проходе вы можете заполнить прыжки с выбранным пессимистическим кодом операции. Затем можно переписать очень мало переходов, чтобы использовать байт или два меньше, только те, которые изначально были очень близки к порогу перехода 8/16 или 16/32 байта. Поскольку все кандидаты являются переходами на несколько байтов, они с меньшей вероятностью попадут в критические циклические ситуации, поэтому вы, вероятно, обнаружите, что дальнейшие проходы дают мало или вообще не дают преимущества по сравнению с двухпроходным решением.

1 голос
/ 12 мая 2010

Вот один из подходов, который я использовал, который может показаться неэффективным, но оказывается, что он не подходит для большинства реальных программ (псевдокод):

IP := 0;
do
{
  done = true;
  while (IP < length)
  {
    if Instr[IP] is jump
      if backwards
      { Target known
          Encode short/long as needed }
      else
      {  Target unknown
          if (!marked as needing long encoding) // see below
            Encode short
          Record location for fixup }
    IP++;
  }
  foreach Fixup do
    if Jump > short
      Mark Jump location as requiring long encoding
      PC := FixupLocation; // restart at instruction that needs size change
      done = false; 
      break; // out of foreach fixup
    else
      encode jump
} while (!done);
...