Principios de composición informática | Análisis línea por línea de llamadas a la pila en código ensamblador

Recopilación de términos técnicos.

  • rbp(Puntero base de registro) es el registro de puntero base , que apunta a la dirección base del marco de pila de la función actual. Un marco de pila es una parte del área de memoria que se utiliza para contener variables locales y otra información relacionada durante las llamadas a funciones. Al rbpguardar la dirección base del marco de pila en , puede acceder fácilmente a los parámetros y variables locales de la función.
  • rsp(Puntero de pila de registro) es el registro del puntero de la pila , que apunta a la posición superior actual de la pila. La pila es una estructura de datos de último en entrar, primero en salir (LIFO) que se utiliza para almacenar datos temporales durante las llamadas a funciones. Al rspguardar la posición superior de la pila en , se puede asignar y liberar espacio de memoria en la pila durante las llamadas a funciones.
  • edx(Registro de datos extendido), registro de datos, utilizado a menudo para almacenar y operar datos , como uno de los registros de uso general. En llamadas a funciones, edxse puede utilizar para almacenar valores de parámetros o variables temporales.
  • eax(Registro de acumulador extendido), acumulador, utilizado para realizar operaciones aritméticas y lógicas y procesar valores de retorno de funciones. eaxLos registros se utilizan a menudo para contener el valor de retorno de una función.
  • esi(Registro de índice de origen) es el registro de índice de origen , generalmente utilizado para almacenar la dirección o el desplazamiento de los datos de origen. A menudo se utiliza en escenarios como operaciones de cadenas y recorrido de bucles.
  • edi(Registro de índice de destino) es el registro de índice de destino , generalmente utilizado para almacenar la dirección o el desplazamiento de los datos de destino. También se usa comúnmente en operaciones de cadenas, recorridos de bucles y otros escenarios.

Un ejemplo simple de lenguaje C: análisis de código ensamblador línea por línea

Tomemos function_example.cpor ejemplo:

// function_example.c
#include <stdio.h>
int static add(int a, int b)
{
    
    
    return a+b;
}


int main()
{
    
    
    int x = 5;
    int y = 10;
    int u = add(x, y);
}

El siguiente es function_example.cel código ensamblador correspondiente:

int static add(int a, int b)
{
    
    
   0:   55                      push   rbp            ; 保存调用者的基址指针
   1:   48 89 e5                mov    rbp,rsp        ; 设置当前函数的基址指针
   4:   89 7d fc                mov    DWORD PTR [rbp-0x4],edi  ; 将第一个参数 a 保存在栈帧中
   7:   89 75 f8                mov    DWORD PTR [rbp-0x8],esi  ; 将第二个参数 b 保存在栈帧中
    return a+b;
   a:   8b 55 fc                mov    edx,DWORD PTR [rbp-0x4] ; 将 a 加载到寄存器 edx
   d:   8b 45 f8                mov    eax,DWORD PTR [rbp-0x8] ; 将 b 加载到寄存器 eax
  10:   01 d0                   add    eax,edx         ; 将 a 和 b 相加并保存在寄存器 eax 中
}
  12:   5d                      pop    rbp             ; 恢复调用者的基址指针
  13:   c3                      ret                     ; 返回至调用者

0000000000000014 <main>:
int main()
{
    
    
  14:   55                      push   rbp            ; 保存调用者的基址指针
  15:   48 89 e5                mov    rbp,rsp        ; 设置当前函数的基址指针
  18:   48 83 ec 10             sub    rsp,0x10       ; 在栈上分配 16 字节的空间
    int x = 5;
  1c:   c7 45 fc 05 00 00 00    mov    DWORD PTR [rbp-0x4],0x5  ; 将值 5 存储在变量 x 的位置上
    int y = 10;
  23:   c7 45 f8 0a 00 00 00    mov    DWORD PTR [rbp-0x8],0xa  ; 将值 10 存储在变量 y 的位置上
    int u = add(x, y);
  2a:   8b 55 f8                mov    edx,DWORD PTR [rbp-0x8] ; 将变量 y 的值加载到寄存器 edx
  2d:   8b 45 fc                mov    eax,DWORD PTR [rbp-0x4] ; 将变量 x 的值加载到寄存器 eax
  30:   89 d6                   mov    esi,edx        ; 将 y 的值复制到 esi 寄存器,作为第二个参数
  32:   89 c7                   mov    edi,eax        ; 将 x 的值复制到 edi 寄存器,作为第一个参数
  34:   e8 c7 ff ff ff          call   0 <add>        ; 调用函数 add
  39:   

Explique el código ensamblador anterior línea por línea, primero addexplique la función:

  1. push rbp: Empuje el puntero de la dirección base de la persona que llama rbpa la pila para guardar la información del marco de la pila de la función de la persona que llama.

  2. mov rbp, rsp: Copie el valor del puntero de pila rspal puntero de dirección base de la función actual rbp, que se utiliza para establecer el marco de pila de la función actual.

  3. mov DWORD PTR [rbp-0x4], edi: Guarde el valor del primer parámetro a(almacenado en el registro edi) en el marco de la pila en el desplazamiento -0x4.

  4. mov DWORD PTR [rbp-0x8], esi: Guarde el valor del segundo parámetro b(almacenado en el registro esi) en el marco de la pila en el desplazamiento -0x8.

  5. mov edx, DWORD PTR [rbp-0x4]-0x4: Cargue el valor (es decir, el parámetro a) en el desplazamiento en el marco de la pila en el registro edx.

  6. mov eax, DWORD PTR [rbp-0x8]-0x8: Cargue el valor (es decir, el parámetro b) en el desplazamiento en el marco de la pila en el registro eax.

  7. add eax, edx: Suma los valores en los registros eaxy , y almacena el resultado en , es decir, se obtiene el resultado de .edxeaxa + b

  8. pop rbp: Extraiga el elemento superior de la pila, que es el puntero de dirección base de la función de llamada rbp, para restaurar el contexto de la función de llamada.

  9. ret: Regresa de la función actual y devuelve el flujo de control a la persona que llama.

A continuación ingrese mainla explicación de la función:

  1. push rbp: Empuje el puntero de la dirección base de la persona que llama rbpa la pila para guardar la información del marco de la pila de la función de la persona que llama.

  2. mov rbp, rsp: Copie el valor del puntero de pila rspal puntero de dirección base de la función actual rbp, que se utiliza para establecer el marco de pila de la función actual.

  3. sub rsp, 0x10: Asigne 16 bytes de espacio en la pila para almacenar variables locales y datos temporales.

  4. mov DWORD PTR [rbp-0x4], 0x5: Almacene el valor 5 en la xubicación de la variable, es decir, el desplazamiento en el marco de la pila -0x4.

  5. mov DWORD PTR [rbp-0x8], 0xa: Almacene el valor 10 en la yubicación de la variable, es decir, el desplazamiento en el marco de la pila -0x8.

  6. mov edx, DWORD PTR [rbp-0x8]: yCarga el valor de la variable en el registro edx.

  7. mov eax, DWORD PTR [rbp-0x4]: xCarga el valor de la variable en el registro eax.

  8. mov esi, edxedx: Copie el valor en el registro (es decir, yel valor de la variable) para registrar esi1 como segundo parámetro.

  9. mov edi, eax: Copie eaxel valor en el registro (es decir, xel valor de la variable) al registro edicomo primer parámetro.

  10. call 0 <add>: Llame a una función addy transfiera el flujo de control al addcódigo de la función.

referencias


  1. Al cargar valores de parámetros en registros, las funciones addpueden leer directamente valores de parámetros de registros para realizar cálculos sin acceder directamente a las variables en la memoria. Esto mejora la eficiencia de ejecución y reduce la cantidad de accesos a la memoria. ↩︎

Supongo que te gusta

Origin blog.csdn.net/YuvalNoah/article/details/131178514
Recomendado
Clasificación