Co

Condicionales en x86-64 Assembly

4 ejercicios

Acerca de Condicionales

RFLAGs

Existe un registro especial llamado rflags. Sus bits actúan como banderas para diversas condiciones.

Algunas de ellas se listan a continuación:

nombre símbolo bit
acarreo CF 0
cero ZF 6
signo SF 7
desbordamiento OF 11

Para ver la lista completa, consulta el manual de Intel.

Instrucciones de comparación

Las banderas de rflags no se modifican directamente. En su lugar, las establecen muchas instrucciones distintas.

Por ejemplo, ZF se establece en muchas operaciones aritméticas o a nivel de bits cuando el resultado es cero.

Una de las instrucciones más comunes para comprobar condiciones es cmp. Toma dos operandos y actualiza las banderas, pero no modifica sus operandos.

La instrucción CMP

La instrucción cmp resta el segundo operando del primero y establece las banderas según el resultado.

Si A es el primer operando y B el segundo:

bandera se establece cuando
CF A < B (sin signo)
ZF A == B
SF A < B (con signo, sin desbordamiento)
OF desbordamiento en una resta con signo

Ramificación

De forma predeterminada, el código en x86-64 se ejecuta de manera secuencial, de arriba hacia abajo.

Pero hay muchas situaciones en las que es necesario modificar este comportamiento. Por ejemplo, para ejecutar un conjunto distinto de instrucciones en respuesta a una condición.

En los lenguajes de alto nivel, esto se suele hacer con abstracciones como los condicionales if...else. Sin embargo, en x86-64 no existen.

En su lugar, x86-64 ofrece instrucciones que transfieren la ejecución a otro punto del código. A esto se le llama branching.

Note

Ya hemos visto dos de esas instrucciones: call y ret.

Cuando se llama a una función, la ejecución se transfiere de quien llama a la función llamada. Y, al retornar, la ejecución se transfiere de vuelta a quien hizo la llamada.

Si no se encuentra un ret, la ejecución pasa directamente de una función a la siguiente. Esto a veces se puede usar para optimizar el flujo del código.

Salto incondicional

La instrucción jmp transfiere la ejecución del programa a otro punto del código de forma incondicional. Su único operando es una etiqueta que contiene la dirección del punto donde continuará la ejecución.

Considera, por ejemplo, la siguiente función:

fn:
    mov rax, 5
    jmp end

    add rax, 10
end:
    ret

Cuando se llama a fn, la ejecución comienza en mov rax, 5. Esto establece el valor de rax en 5 en ese punto.

La siguiente instrucción es jmp end, que transfiere la ejecución a la etiqueta end.

Después de end, la siguiente instrucción es ret, que transfiere la ejecución de vuelta a la función que hizo la llamada.

Fíjate que, como add rax, 10 está ubicada después de jmp end y antes de end, nunca se ejecuta. El valor de rax cuando fn retorna es 5.

Salto condicional

La familia de instrucciones jcc transfiere la ejecución del programa a otro punto solo si se cumple una condición específica. En caso contrario, la ejecución continúa de forma secuencial.

Cada condición se corresponde con una o más banderas de rflags. Algunas variantes de jcc comprueban que una bandera está establecida; otras, que está borrada.

El cc de jcc no es literal, sino que hace referencia al sufijo específico asociado a la bandera que se comprueba.

Hay muchos sufijos, y muchos de ellos comprueban la misma condición que otro. Algunos se refieren directamente a una bandera, de modo que la instrucción salta a una etiqueta si esa bandera está establecida:

sufijo salta si
z ZF == 1
c CF == 1
s SF == 1
o OF == 1

Muchos otros se eligen para hacer referencia a su significado en una instrucción cmp. Por ejemplo:

instrucción sufijo salta si
cmp A, B e A == B
cmp A, B l A < B (con signo)
cmp A, B b A < B (sin signo)
cmp A, B g A > B (con signo)
cmp A, B a A > B (sin signo)

Es posible agregar e después de l, b, g o a para incluir la igualdad en la condición:

cmp rcx, r8
jge two      ; this jumps to 'two' if rcx is greater than, or equal to, r8 in a signed comparison
jbe two      ; this jumps to 'two' if rcx is lesser than, or equal to, r8 in an unsigned comparison

Para todos los sufijos hay variantes que comprueban el comportamiento opuesto. Tienen la misma sintaxis, pero con una n antes del sufijo.

Por ejemplo, jnz salta cuando ZF no está establecida. De manera similar, jnae salta cuando A no es >= B (A y B interpretados como enteros sin signo).

Note

Algunos sufijos son alias de las mismas condiciones. Por ejemplo, jz (sufijo z, para ZF) y je (sufijo e, para igualdad) saltan ambos cuando ZF está establecida. Esto se debe a que, con cmp, ZF se establece cuando la resta da cero, lo que corresponde a que los dos operandos sean iguales.

Sin embargo, otros sufijos comprueban una combinación de banderas y no se pueden sustituir directamente por un único sufijo de bandera.

Prefiere el sufijo que describa mejor la semántica de tu comparación.

Etiquetas locales

Las etiquetas son visibles en todo el archivo fuente; no son locales a una función. Por eso es imposible reutilizar el nombre de una etiqueta.

Para imitar el comportamiento de una etiqueta local, NASM tiene una notación especial para una etiqueta declarada con un punto (.) delante. Esta notación define una etiqueta que incluye implícitamente el nombre de la etiqueta anterior sin punto:

section .text
fn1:
    ...
.example: ; this is fn1.example
    ...
    ret

fn2:
    ...
.example: ; this is fn2.example
    ...
    ret

Todavía es posible saltar a esta etiqueta desde cualquier lugar del código usando el nombre completo de la etiqueta; por ejemplo, jmp fn1.example.

Sin embargo, un salto que use la parte de la etiqueta a partir del punto se hará a la etiqueta que está dentro de la función superior. Por ejemplo, .example se comporta como si fuera local a la función:

section .text
fn1:
    ...
.example:
    ...
    jmp .example ; this jumps to fn1.example

fn2:
    ...
.example:
    ...
    jmp .example ; this jumps to fn2.example

Fíjate que una etiqueta sin punto dentro de una función, en la práctica, define otra función:

section .text
fn1:
    ...
.example: ; this is 'fn1.example'
    ...
non_dotted:
    ...
.example: ; this is 'non_dotted.example'
    ...
    ret
Editar en GitHub El enlace se abre en una ventana o pestaña nueva

Aprende Condicionales