2010-05-11 27 views
8

Mientras que la construcción de mi ensamblador para la plataforma x86 me encontré con algunos problemas con que codifica la JMP instrucción:¿Cómo se implementa un JMP (x86) relativo en un ensamblador?

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

(de mi sitio web favorito de instrucciones x86, http://siyobik.info/index.php?module=x86&id=147)

Todos son relativos saltos, donde el tamaño de cada codificación (operación + operando) está en la tercera columna.

Ahora mi original (y por tanto de fallos debido a esto) el diseño se reservó el espacio máximo (5 bytes) para cada instrucción. El operando aún no se conoce porque es un salto a una ubicación aún desconocida. Así que implementé un mecanismo de "reescritura", que reescribe los operandos en la ubicación correcta de la memoria, si se conoce la ubicación del salto, y rellena el resto con NOP s. Esta es una preocupación un tanto seria en tight-loops.

Ahora mi problema es con la siguiente situación:

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) 

El problema es que quiero la codificación más pequeña posible para una instrucción JMP (y sin NOP llenado).

Tengo que saber el tamaño de la instrucción en c antes de que pueda calcular la distancia relativa entre a y b para el operando en d. Lo mismo se aplica para el JMP en c: lo que necesita saber el tamaño de d antes de que pueda calcular la distancia relativa entre e y a.

¿Cómo montadores existentes resuelven este problema, o ¿cómo hacer esto?

Esto es lo que estoy pensando que resuelve el problema:

Primera codificar todas las instrucciones a los códigos de operación entre el JMP y su objetivo, en esta región contiene un código de operación de tamaño variable, utilice el tamaño máximo , p.ej 5 para un JMP. Luego codifique el relativo JMP en su objetivo, eligiendo el tamaño de codificación más pequeño posible (3, 4 o 5) y calcule la distancia. Si se codifica cualquier código de operación de tamaño variable, cambie todos los operandos absolutos antes, y todas las instrucciones relativas que omitan esta instrucción codificada: se vuelven a codificar cuando su operando cambia para elegir el tamaño más pequeño posible. Se garantiza que este método finalizará, ya que los códigos de operación de tamaño variable solo pueden reducirse (porque usan el tamaño máximo de los mismos).

Me pregunto, tal vez esta es una solución más de la ingeniería, por eso hago esta pregunta.

+0

+1 para el enlace bonita documentación asm –

Respuesta

1

Aquí es uno de los enfoques que he usado que puede parecer ineficiente, pero resulta que no sea para la mayoría del código de la vida real (pseudo-código):

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); 
+0

¡Aseado! Sin embargo, pero no se puede saber eso, mi ensamblador y compilador se ejecutan en paralelo, por lo que es posible que el código se inserte entre un objetivo y un salto relativo hacia atrás. Pero el bucle de dos pasos es un muy buen enfoque, gracias. (También PC = IP en el bucle de reparación, supongo?) – Pindatjuh

+1

Oh sí, lo siento - PC = IP. –

+0

En realidad, acabo de recordar que hay una ligera complicación con esto: cuando retrocedes y cambias el tamaño de un salto, también debes tener en cuenta cualquier salto corto hacia adelante sobre esta ubicación que estás a punto de expandir y que ahora ya no puede alcanzar sus objetivos –

3

En la primera pasada, tendrá una muy buena aproximación a qué código jmp usar usando un conteo de bytes pesimista para todas las instrucciones de salto.

En el segundo paso se puede rellenar los saltos con el código de operación pesimista elegido. Muy pocos saltos podrían reescribirse para usar un byte o dos menos, solo aquellos que estaban muy cerca del umbral de salto de 8/16 bit o 16/32 byte originalmente.Como los candidatos son todos saltos de muchos bytes, es menos probable que estén en situaciones de bucle crítico, por lo que es probable que descubras que los pases adicionales ofrecen poco o ningún beneficio en comparación con una solución de dos pases.

+0

Gran respuesta : pero ¿por qué es menos probable que el límite de 128 bytes (entre 8/16 bit) esté en situaciones de lazo crítico? Puedo imaginar una situación de bucle crítico de exactamente 128 bytes, que se ejecutará más rápido que con un salto de 16 bits. ¿O es esta optimización prematura? – Pindatjuh

+1

Bueno, lo que quiero decir con un ciclo crítico es aquel en el que la prueba y el salto del ciclo son una parte importante del código del ciclo. 128 bytes es muy largo para tal bucle, la mayoría de los bucles críticos serán unos pocos bytes y cualquier salto de un bucle es probable que también sea pequeño. –

Cuestiones relacionadas