JMP vs CALL en ensamblador x86: La pila marca toda la diferencia
En ensamblador x86, tanto JMP como CALL transfieren el control a otra ubicación de código, pero su comportamiento con la pila crea semántica de flujo de control fundamentalmente diferente. Entender esta distinción es esencial para escribir subrutinas, implementar recursividad y depurar flujo de control.
Diferencia central: Interacción con la pila
| Aspecto | JMP destino | CALL destino |
|---|---|---|
| Efecto en pila | Ninguno — pila sin cambios | Apila dirección de retorno (EIP/RIP) |
| Mecanismo de retorno | Ninguno — transferencia unidireccional | RET desapila dirección, reanuda después de CALL |
| Uso típico | Bucles, condicionales, saltos incondicionales | Subrutinas, funciones, procedimientos |
| Soporte de anidamiento | No — no se puede anidar limpiamente | Sí — la pila permite anidamiento arbitrario |
| Recursividad | Imposible sin manejo manual de pila | Soporte nativo mediante marcos de pila |
Codificación de instrucciones (x86-64)
; Variantes JMP EB cb JMP rel8 ; Salto corto (-128 a +127 bytes) E9 cw JMP rel16/32 ; Salto cercano (relativo) FF /4 JMP r/m16/32/64 ; Salto indirecto (registro/memoria) EA cd JMP ptr16:16/32 ; Salto lejano (segmento:desplazamiento) - legado ; Variantes CALL E8 cw CALL rel16/32 ; Llamada cercana (relativa, apila RIP) FF /2 CALL r/m16/32/64 ; Llamada indirecta 9A cd CALL ptr16:16/32 ; Llamada lejana (legado)
Mecánica de marcos de pila
Flujo de ejecución de CALL
; Antes de CALL RIP = dirección de la siguiente instrucción RSP = puntero de pila actual ; CALL objetivo ejecuta: 1. RSP ← RSP - 8 ; Decrementar puntero de pila (64-bit) 2. [RSP] ← RIP ; Apilar dirección de retorno (siguiente instrucción) 3. RIP ← objetivo ; Saltar a subrutina ; Dentro de la subrutina (prólogo típico): PUSH RBP ; Guardar base pointer del llamador MOV RBP, RSP ; Establecer nuevo frame pointer SUB RSP, N ; Asignar espacio de pila local ; Ejecución de RET: 1. RIP ← [RSP] ; Desapilar dirección de retorno 2. RSP ← RSP + 8 ; Restaurar puntero de pila 3. La ejecución reanuda después de CALL
Flujo de ejecución de JMP
; JMP objetivo ejecuta: 1. RIP ← objetivo ; Salto incondicional ; El puntero de pila (RSP) no cambia ; No se almacena dirección de retorno ; No hay forma de regresar automáticamente
Implicaciones prácticas
Patrón de subrutina (CALL/RET)
; Llamador CALL calculate_sum ; Apila dirección de retorno, salta ; ... se reanuda aquí después de RET ; Subrutina calculate_sum: PUSH RBP MOV RBP, RSP ; ... cálculo ... MOV RAX, resultado POP RBP RET ; Desapila dirección de retorno, salta de vuelta
Patrón de bucle/máquina de estados (JMP)
state_loop: CMP AL, 'A' JE handle_A CMP AL, 'B' JE handle_B JMP default_handler handle_A: ; ... procesar A ... JMP next_char handle_B: ; ... procesar B ... JMP next_char default_handler: ; ... error ... JMP next_char next_char: ; ... obtener siguiente ... JMP state_loop ; Volver al bucle
Avanzado: Optimización de llamada en cola
Los compiladores a veces reemplazan la secuencia CALL ... RET con JMP cuando la última acción de una función es llamar a otra función:
; Sin optimización foo: CALL bar RET ; Con optimización de llamada en cola (TCO) foo: JMP bar ; Reutiliza el marco de pila del llamador
Beneficios: Ahorro de espacio de pila, retorno más rápido. Requiere: sin limpieza después de la llamada, misma convención de llamada.
Consideraciones de seguridad
- Return-Oriented Programming (ROP): Explotaciones de cadenas
RET—CALLcrea direcciones de retorno predecibles - Control Flow Integrity (CFI): Hardware (Intel CET, ARM PAC) valida objetivos de
CALL/RET - Shadow Stacks: Pila separada protegida para direcciones de retorno (CET)
Resumen
- JMP = "Ve allí" (sin retorno, sin cambio de pila)
- CALL = "Ve allí, regresa aquí" (apila dirección de retorno, habilita
RET) - CALL/RET = Base de la programación procedural, recursividad, código modular
- JMP = Base de bucles, máquinas de estado, flujo condicional
Cada llamada a función de alto nivel (func() en C, def en Python, method() en Java) se compila a la maquinaria CALL/RET. Cada while, for, if, goto se compila a variantes de JMP. La pila es lo que hace poderoso a CALL—y lo que hace peligrosos los desbordamientos de búfer.
- ·Acciones de Horry Telephone Cooperative: por qué no encontrarás un precio de acción
- ·Cómo instalar una nueva línea telefónica: guía completa paso a paso
- ·Cómo hacer una llamada a cargo del destinatario
- ·Cómo solucionar problemas de un Conex DT-55
- ·Cómo reducir la factura del teléfono
- ·Qué ofrece Internet a los compradores online: 10 ventajas clave
- ·Cómo dividir un cable telefónico
- ·Cómo configurar un auricular para hablar por magicJack
- Cómo empalmar un cable telefónico
- Cómo transferir un mensaje de buzón de voz a cinta
- Cómo encontrar el número de móvil de alguien desde su dirección (legalmente)
- Aire acondicionado portátil iCoolite: No se necesita línea telefónica, se enchufa a la pared
- Cómo saltarse el TeleZapper
- Beneficios de los calcetines para teléfono (fundas de tela)
