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. Alrbp
guardar 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. Alrsp
guardar 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,edx
se 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.eax
Los 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.c
por 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.c
el 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 add
explique la función:
-
push rbp
: Empuje el puntero de la dirección base de la persona que llamarbp
a la pila para guardar la información del marco de la pila de la función de la persona que llama. -
mov rbp, rsp
: Copie el valor del puntero de pilarsp
al puntero de dirección base de la función actualrbp
, que se utiliza para establecer el marco de pila de la función actual. -
mov DWORD PTR [rbp-0x4], edi
: Guarde el valor del primer parámetroa
(almacenado en el registroedi
) en el marco de la pila en el desplazamiento-0x4
. -
mov DWORD PTR [rbp-0x8], esi
: Guarde el valor del segundo parámetrob
(almacenado en el registroesi
) en el marco de la pila en el desplazamiento-0x8
. -
mov edx, DWORD PTR [rbp-0x4]
-0x4
: Cargue el valor (es decir, el parámetroa
) en el desplazamiento en el marco de la pila en el registroedx
. -
mov eax, DWORD PTR [rbp-0x8]
-0x8
: Cargue el valor (es decir, el parámetrob
) en el desplazamiento en el marco de la pila en el registroeax
. -
add eax, edx
: Suma los valores en los registroseax
y , y almacena el resultado en , es decir, se obtiene el resultado de .edx
eax
a + b
-
pop rbp
: Extraiga el elemento superior de la pila, que es el puntero de dirección base de la función de llamadarbp
, para restaurar el contexto de la función de llamada. -
ret
: Regresa de la función actual y devuelve el flujo de control a la persona que llama.
A continuación ingrese main
la explicación de la función:
-
push rbp
: Empuje el puntero de la dirección base de la persona que llamarbp
a la pila para guardar la información del marco de la pila de la función de la persona que llama. -
mov rbp, rsp
: Copie el valor del puntero de pilarsp
al puntero de dirección base de la función actualrbp
, que se utiliza para establecer el marco de pila de la función actual. -
sub rsp, 0x10
: Asigne 16 bytes de espacio en la pila para almacenar variables locales y datos temporales. -
mov DWORD PTR [rbp-0x4], 0x5
: Almacene el valor 5 en lax
ubicación de la variable, es decir, el desplazamiento en el marco de la pila-0x4
. -
mov DWORD PTR [rbp-0x8], 0xa
: Almacene el valor 10 en lay
ubicación de la variable, es decir, el desplazamiento en el marco de la pila-0x8
. -
mov edx, DWORD PTR [rbp-0x8]
:y
Carga el valor de la variable en el registroedx
. -
mov eax, DWORD PTR [rbp-0x4]
:x
Carga el valor de la variable en el registroeax
. -
mov esi, edx
edx
: Copie el valor en el registro (es decir,y
el valor de la variable) para registraresi
1 como segundo parámetro. -
mov edi, eax
: Copieeax
el valor en el registro (es decir,x
el valor de la variable) al registroedi
como primer parámetro. -
call 0 <add>
: Llame a una funciónadd
y transfiera el flujo de control aladd
código de la función.
referencias
Al cargar valores de parámetros en registros, las funciones
add
pueden 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. ↩︎