©2013 Mario Medina C. 1

Anuncio
Ensamblador y lenguaje C
Š Lenguaje C: lenguaje de alto nivel
z
z
Ensamblador Intel x86 y lenguaje C
Sistemas Computacionales
Mario Medina C.
[email protected]
z
Š Lenguaje de ensamblador: lenguaje de bajo nivel
z
z
Código C y ensamblador x86
z
z
z
Saltos directos e indirectos
Condicionales e incondicionales
A direcciones absolutas y relativas
Š Lenguaje ensamblador no posee estructuras de
control complejas
z
Pero, éstas se pueden implementar utilizando
saltos
Correspondencia 1-a-1 con código de máquina
Usado para optimizar código
Š Veremos cómo traducir código C a código
ensamblador equivalente
z
Š Lenguaje ensamblador Intel x86 posee gran
variedad de saltos
Fácil de compilar a código de máquina
Usado para escribir sistemas operativos
El lenguaje de alto nivel más cercano a la máquina
Ayuda a entender cómo funciona un programa
Código C y ensamblador x86
Š Lenguaje de programación C posee estructuras
de control complejas
z
z
z
z
z
z
if-else
do-while
while
for
goto
Cómo traducir estas estructuras a lenguaje de
máquina?
œUtilizando sólo instrucciones goto
Transformando if-else
Š Código C original
z
Calcula valor absoluto de la diferencia entre dos
números
int absdiff(int x, int y) {
if (x < y)
return y – x;
else
return x – y;
}
©2013 Mario Medina C.
Transformando if-else
Š Función recibe 2 argumentos
z
z
Tiene 2 valores de retorno posibles
Flujo de control puede tomar 2 caminos
Š Reescribir usando sólo instrucciones
goto
Š No son recomendados en C, pero son
muy usados en ensamblador!
z
Fáciles de traducir a ensamblador
1
Transformando if-else
Š Código C modificado
int gotodiff(int x, int y) {
int rval;
if (x < y)
goto menor;
rval = x – y;
goto listo;
menor:
rval = y – x;
listo:
return rval;
}
Transformando if-else
Š Nueva versión del código utiliza sólo
instrucción goto
z
z
movl 8(%ebp), %edx
movl 12(%ebp), %eax
cmpl %eax, %edx
jl .menor
subl %eax, %edx
movl %edx, %eax
jmp .listo
.menor:
subl %edx, %eax
.listo:
//
//
//
//
//
//
//
//
//
//
Lee x
Lee y
Compara x e y
Si <, ir a menor:
Calcula x-y
Valor de retorno
Ir a listo:
menor:
Calcula y-x
listo:
if-else con doble condición
Š Código C modificado
Š Evalúa primero condición de la izquierda y
luego la de la derecha
Š Cumple condición de cortocircuito
void cond(int a, int *p) {
if (p != 0) {
if(a > 0) {
*p = *p + a;
}
}
}
©2013 Mario Medina C.
Valor a retornar en rval
Š Un punto de entrada a la función
Š Un punto de salida de la función
if-else con doble condición
Transformando if-else
•Código ensamblador x86 equivalente
Requiere 2 goto y 2 rótulos, menor y listo
Š El flujo de control de las instrucciones está en
forma explícita
Š Un solo valor de retorno posible
Š Código C original
void cond(int a, int *p)
{
if (p && a > 0)
*p += a;
}
z
if-else con condición doble
œLenguaje C evalúa primero la condición de la
izquierda
œDescomponer condición doble en condiciones
simples
if-else con doble condición
Š Código ensamblador equivalente
movl 8(%ebp), %edx
movl 12(%ebp), %eax
testl %eax, %eax
jz .Fin
testl %edx, %edx
jle .Fin
addl %edx, (%eax)
.Fin:
//
//
//
//
//
//
//
Copia a en %edx
Copia p en %eax
Calcula p&p
Salta si p es 0
Calcula a&a
Salta si a <= 0
*p = *p + a
2
Lazos do-while
Š Tipo de lazo muy usado en lenguaje de
máquina
z
No tan utilizado en lenguaje C
do
{
nucleo del lazo;
} while(condicion);
Š Se convierte en código C
equivalente que usa sólo ifs
y gotos
Ejemplo: Fibonacci con do-while
Š Secuencia de Fibonacci
z
z
rotulo:
{
nucleo del lazo;
}
if (condicion)
goto rotulo;
z
F0 = 0
F1 = 1
Fn = Fn-1 + Fn-2, n≥2
Š Implementación en C con ciclo do-while
z
z
z
z
val almacena Fo = 0
nval almacena F1 = 1
t almacena F2 = val + nval
Función retorna Fp, donde p es el argumento
Ejemplo: Fibonacci con do-while
int fib_dowhile(int p)
{
int i = 0, t;
int val = 0;
int nval = 1;
do {
t = val + nval;
val = nval;
nval = t;
i++;
} while (i < p);
return val;
// Inicializa con F_0
// Inicializa con F_1
Ejemplo: Fibonacci
Tabla de registros y
variables
// Calcula F_n
// Actualiza F_n-2
// Actualiza F_n-1
Registro Variable Valor
inicial
%ecx
i
0
%esi
p
p
%ebx
val
0
%edx
nval
1
// Retorna F_n-2
%eax
t
-
}
Lazos while
Š Puede implementarse como do-while
Š Lazo while
Š Lazo do-while
while(condicion)
{
nucleo del lazo;
}
if (!condicion)
goto listo;
do
{
nucleo del lazo;
} while(condicion);
listo:
Š Sacar la primera verificación
de la condicion del lazo
©2013 Mario Medina C.
Š Código ensamblador que
implementa Fibonacci
.L1:
leal (%edx,%ebx),%eax
movl %edx, %ebx
movl %eax, %edx
incl %ecx
cmpl %esi, %ecx
jl .L1
movl %ebx, %eax
Ejemplo: Fibonacci con while
int fib_while(int p)
{
int i = 1, t;
int val = 1;
int nval = 1;
while (i < p) {
t = val + nval;
val = nval;
nval = t;
i++;
}
return val;
}
// Inicializa con F_1
// Inicializa con F_2
// Calcula F_n
// Actualiza F_n-2
// Actualiza F_n-1
// Retorna F_n-2
3
Ejemplo: Fibonacci con while
Š Ciclo while verifica condición antes de
ejecutar el núcleo del lazo
z
z
Si condición no se cumple, el lazo no se ejecuta
A diferencia de ciclo do_while
Š Condiciones iniciales comienzan con F1 y F2
z
Valor inicial de i es 1
Š Para pasarlo a ensamblador, primero debemos
reescribirlo para usar comandos goto
Ejemplo: Fibonacci con while
int fib_while_goto(int n)
{
int i = 1;
int val = 1;
// Inicializa con F_1
int nmi = n - 1, t;
if (val >= n) goto listo;
lazo: t = val + nval;
// Calcula F_n
val = nval;
// Actualiza F_n-2
nval = t;
// Actualiza F_n-1
nmi--;
// Calcula n - i
if (nmi) goto lazo;
listo:
return val;
// Inicializa con F_n-2
}
Ejemplo: Fibonacci con while
Š Versión de ciclo while utilizando
instrucciones goto
Š Código calcula n – i y usa valor nmi como
índice del lazo
z
z
z
Se detiene cuando nmi es 0
Usa 1 registro menos
Elimina variable i
Ejemplo: Fibonacci con while
Registro
Variable
Valor inicial
%edx
nmi
n
%ebx
val
1
%ecx
nval
1
movl 8(%ebp), %eax
movl $1, %ebx
movl $1, %ecx
cmpl %eax, %ebx
jge .Listo
leal -1(%eax), %edx
.Lazo:
leal (%ecx,%ebx), %eax
movl %ecx, %ebx
movl %eax, %ecx
decl %edx
jnz .Lazo
.Listo
- 1
•Optimizaciones
•Código usa 1 registro
menos
•Elimina variable i
Lazos for
Š Forma general de lazo iterativo
z
Equivalente a lazo while
Š Lazo for
Š Lazo while
for (expr1; expr2; expr3)
{
nucleo del lazo;
}
expr1;
while(expr2)
{
nucleo del lazo;
expr3;
}
Ejemplo: Fibonacci con for
Š Lazo for con goto
expr1;
if (!expr2)
goto listo;
lazo:
{
nucleo del lazo;
expr3;
}
if (expr2)
goto lazo;
listo:
int fib_for(int n)
{
int i = 1, t;
int val = 1;
int nval = 1;
for (i = 0; i < n; i++) {
t = val + nval;
val = nval;
nval = t;
}
return val;
}
Š Código x86 es similar a while
©2013 Mario Medina C.
4
Estructura de control switch
Š Estructuras switch(i) implementan saltos
multivías
z
z
Puede ser modelado como series de if-else
Generalmente se implementa como tablas de
saltos
Ejemplo: switch
int switch_s(int x)
{
switch(x) {
case 100:
x *= 13;
break;
case 104:
case 106:
x *= x;
break;
default:
x = 0;
œVector de direcciones indexado por índice i
z
case 102:
x += 10;
case 103:
x += 11;
break;
Más eficiente que varios if-else
Ejemplo: switch
int switch_tabla(int x)
{
unsigned xi = x – 100;
if (xi > 6)
goto dir_default;
/* C invalido */
goto tabla[xi];
dir_A:
x *= 13;
goto listo;
dir_B:
x += 10;
Š Estructura de control switch se transforma
en saltos incondicionales
dir_C:
x += 11;
goto listo;
dir_D:
x *= x;
goto listo;
dir_default:
x = 0;
listo:
return x;
}
z
Direcciones de destino están almacenadas en tabla
en memoria
Variable x es la variable del switch
z
Variable xi es un índice a la tabla de salto
z
œSe calcula xi como x - 100
œVariable es de tipo unsigned
œSi xi > 6, ir a dir_default
/* Esto no es C válido */
Š Tabla contiene direcciones
de salto que dependen de x
©2013 Mario Medina C.
}
Ejemplo: switch
Ejemplo: switch
tabla[7] = {
dir_A,
dir_default,
dir_B,
dir_C,
dir_D,
dir_default,
dir_D;
}
return x;
tabla[0]
dir_A
tabla[1]
dir_default
tabla[2]
dir_B
tabla[3]
dir_C
tabla[4]
dir_D
tabla[5]
dir_default
tabla[6]
dir_D
Ejemplo: switch
leal -100(%edx), %eax
cmpl $6, %eax
ja .Ldef
jmp *.Ltbl(, %eax, 4)
.L103: // Caso 103
addl $11, %edx
jmp .Lret
.L104: // Casos 104 y 106
imull %edx, %edx
.L100:
// Caso 100
jmp .Lret
leal (%edx, %edx, 2), %eax
leal (%edx, %eax, 4), %edx
.Ldef // Caso default
jmp .Lret
xorl %edx, %edx
.L102: // Caso 102
.Lret // retorno
addl $10, %edx
movl %edx, %eax
5
Ejemplo: switch
.Ltbl: // Tabla de salto
.long .L100 // Caso 100
.long .Ldef // Caso 101
.long .L102 // Caso 102
.long .L103 // Caso 103
.long .L104 // Caso 104
.long .Ldef // Caso 105
.long .L104 // Caso 106
Š Compilador genera una
tabla estática en memoria
Š Entradas son direcciones a
dónde saltar
z
Suponiendo un procesador
de 32 bits, cada entrada
son 4 bytes
Ejercicio: switch
movl 8(%ebp), %eax
addl $2, %eax
cmpl $6, %eax
ja .L10
jmp *.L11, (, %eax, 4)
Š Ejercicio:
z
Sea el siguiente switch()
incompleto y el código
ensamblador equivalente,
complete el código C
.L11: // Tabla de salto
.long .L4
.long .L10
.long .L5
.long .L6
.long .L8
.long .L8
.long .L9
int switch2(int x) {
int result = 0;
switch(x) {
/* Completar */
}
return result;
}
Vectores
Vectores
Š Vectores y matrices en C son almacenados
primero por filas y luego por columnas
z
z
Fácil acceso a elementos del vector
Modos de direccionamiento indexado escalado
Š Ejemplo:
tipo Vector[N];
z
Reserva una área contigua de memoria de L*N
bytes apuntada por Vector
œL: tamaño de tipo en bytes
Sean los vectores
(proc. 32 bits)
char A[12];
char *B[8];
double C[6];
double *D[5];
int E[10];
Vector
Tamaño
elemento
Tamaño Dir.
total
Inicial
Elemento i
A
1
12
xA
xA+i
B
4
32
xB
xB+4i
C
8
48
xC
xC+8i
D
4
20
xD
xD+4i
E
4
40
xE
xE+4i
Aritmética de punteros
Š Dada la declaración int E[10], calcule las siguientes
expresiones en código ensamblador
z
Suponga que %edx contiene la dirección inicial de E, %ecx
contiene el índice i y %eax debe contener la expresión
Expresión
Tipo
Valor
Código
E
int *
xE
movl %edx,%eax
E[0]
int
M[xE]
movl (%edx),%eax
E[i]
int
M[xE+4i]
movl (%edx,%ecx,4),%eax
&E[2]
int *
xE+8
leal 8(%edx),%eax
E+i-1
int *
xE+4i-4
leal -4(%edx,%ecx,4),%eax
*(&E[i]+i) int
M[xE+4i+4i]
movl (%edx,%ecx,8),%eax
&E[i] - E
i
movl %ecx,%eax
int
©2013 Mario Medina C.
Lazos y vectores
Š Patrones muy regulares
z
Usa aritmética de punteros para recorrer vector
œComparación con dirección final
int decimal5(int *x)
{
int i;
int val = 0;
for (i = 0; i < 5; i++)
val = 10*val + x[i];
return val;
}
Š Función convierte un vector
de 5 enteros en el entero
equivalente
x
1
2
3
4
5
12345
6
Lazos y vectores
int decimal5_dw(int *x)
{
int i, val = 0;
int *xend = x + 4;
do {
val = 10*val + *x;
x++;
while(x <= xend);
return val;
}
Código ensamblador
Š Puntero xend apunta
a último elemento del
vector
Š Puntero x avanza hasta
que sea igual a xend
movl 8(%ebp),%ecx
xorl %eax,%eax
leal 16(%ecx),%ebx
.Lazo:
leal (%eax,%eax,4),%edx
movl (%ecx),%eax
leal (%eax,%edx,2),%eax
addl $4,%ecx
cmpl %ebx,%ecx
jbe .Lazo
Matrices en C
Š Declaración
Elemento
int A[4][3];
Š La matriz A contiene 4 elementos
z
Cada elemento es un vector de 3
enteros
Memoria total: 4*3*4=48 bytes
tipo D[F][C];
z
A[0][2] xA+8
A[1][0] xA+12
A[1][2] xA+20
z
A[2][0] xA+24
sall
leal
leal
movl
A[2][2] xA+32
A[3][0] xA+36
A[3][2] xA+44
Matrices de tamaño fijo
z
Acceder directamente a elementos de la matriz
mediante punteros a filas y/o columnas
Calcular direcciones de elementos en forma
estática
Acceso a elemento Ai,j en matriz anterior
œxA en %eax, i en %edx, j en %ecx, tipo es int
A[2][1] xA+28
A[3][1] xA+40
z
reserva una área contigua de memoria de L*F*C
bytes apuntada por D
œL: tamaño de tipo en bytes
A[1][1] xA+16
Š Si las matrices a usar son de tamaño fijo,
compilador puede optimizar acceso a
elementos
%edx = 5*val
lee *x
lee *x + 10*val
x++
compara x y xend
Si <=, ir a Lazo
Š Declaración de matriz
A[0][1] xA+4
typedef int fila3_t[3];
fila3_t A[4];
//
//
//
//
//
//
Acceso a elementos de matrices
A[0][0] xA
Š Es equivalente a
z
Dirección
// Lee dir. vector X
// val = 0
// xend = x + 4
$2,%ecx
(%edx,%edx,2),%edx
(%ecx,edx,4), %edx
(%eax,%edx),%eax
//
//
//
//
j*4
i*3
j*4 + i*12
lee M[A + j*4 + i*12]
Optimizaciones y matrices
#define N 16
typedef int mat16[N][N];
int prodMatriz16(mat16 A, mat16 B, int i, int k)
{
int j;
int result = 0;
for (j = 0; j < N; j++)
result += A[i][j]*B[j][k];
return result;
œUsando desplazamientos y sumas
}
©2013 Mario Medina C.
7
Cálculo del elemento C[i][k]
Matriz A
Matriz B
Fila
A[i][ ]
Columna B[ ][k]
Optimizaciones y matrices
int prodMatriz16_opt(mat16 A, mat16 B, int i, int k) {
int *Aptr = &A[i][0];
int *Bptr = &B[0][k];
int count = N – 1;
int result = 0;
do {
result += (*Aptr)*(*Bptr);
Aptr += 1;
Bptr += N;
count--;
} while (count >= 0);
return result;
}
Optimizaciones y matrices
.Lazo:
movl (%edx), %eax
imull (%ecx), %eax
addl %eax, %esi
addl $64, %ecx
addl $4, %edx
decl %ebx
jns .Lazo
//
//
//
//
//
//
//
Lee *Aptr
calcula (*Bptr)*(*Aptr)
Suma A[i]*B[i] a result
Incrementa Bptr
Incrementa Aptr
Decrementa count
Si >=, ir a Lazo
Š Suponemos Aptr en %edx, Bptr en %ecx,
result en %esi, count en %ebx
Código ejemplo
Š Código C original
z
Inicializa los elementos
diagonales de A
movl 12(%ebp),%edx
movl 8(%ebp),%eax
void initDiag(mat16 A, int val)
movl $15,%ecx
{
.Lazo:
int i;
movl %edx,(%eax)
for (i = 0; i < N; i++)
addl $68,%eax
A[i][i] = val;
decl %ecx
}
jns .Lazo
Memoria dinámica
Š Asignación dinámica de memoria
z
z
Obtenida via malloc() o calloc()
Liberada via free()
Š Vectores y/o matrices cuyo tamaño se
determinan en tiempo de ejecución
z
z
Matrices se simulan como vectores
Si A es una matriz de nxm, se asigna como
A = calloc(sizeof(int), n*m);
©2013 Mario Medina C.
Š Código ensamblador
Acceso a elementos de matrices
Š Acceso a elemento Ai,j en matriz anterior
z
Cálculo del índice al vector debe usar
multiplicaciones
A[i*m + j]
z
Ejemplo de acceso a matriz
œSuponemos que A, i, m y j están en la pila
movl 8(%ebp),%edx
movl 12(%ebp),%eax
imull 20(%ebp),%eax
addl 16(%ebp),%eax
movl (%edx,%eax,4),%eax
//
//
//
//
//
Lee A
Lee i
Calcula m*i
Calcula m*i + j
Lee A[m*i + j]
8
Ejemplo: prodMatriz()
typedef int *varMat;
int prodMatriz(varMat A, varMat B, int i, int k, int n)
{
int j;
int result = 0;
for (j = 0; j < N; j++)
result += A[i*n + j]*B[j*n + k];
return result;
}
Š Suponemos que A y B son punteros a vectores de int
Š Convertir ahora a ciclo do-while
Ejemplo: prodMatriz()
int prodMatOpt(varMat A, varMat B, int i, int k, int n) {
int *Aptr = &A[i*n]
int temp = n;
int count = n;
int result = 0;
if (n <= 0) return result;
do {
result += (*Aptr) * B[temp];
Aptr += 1;
temp += n;
count --;
} while (count != 0);
return result;
}
Ejemplo: prodMatriz()
.L10:
movl 12(%ebp),%eax
movl (%ebx),%edi
addl $4, %ebx
imull (%eax,%ecx,4),%edi
addl %edi,%esi
addl 24(%ebp),%ecx
decl %edx
jns .L10
z
//
//
//
//
//
//
//
//
Lee B
Lee *Aptr
Incrementa Aptr
Multiplica por B[temp]
Acumula resultado
Sumar n a temp
Incrementa count
Si count != 0, ir a L10
Suponemos Aptr en %ebx, temp en %ecx, result en
%esi, count en %edx
Estructuras de datos
Š struct crea un tipo de dato compuesto que
agrupa múltiples objetos de tipos diferentes
z
z
z
Componentes de estructura referenciados por
nombre
Componentes de estructura se almacenan en
memoria en forma contigua
Puntero a estructura es dirección del primer byte
de la estructura
œtemp calcula n*j+k
œB y n se leen desde memoria porque no alcanzan los registros
Estructuras de datos
struct rec {
int i;
int j;
int a[3];
int *p;
};
struct rec r;
struct rec *sr = r;
r.i = 30;
(*sr).j = 10;
sr->a[0] = r.i;
©2013 Mario Medina C.
sr
struct rec r
Estructuras de datos
Š Supongamos que sr está en %edx, i en
%esi
Š Copiar sr->i en sr->j
movl (%edx),%eax
i
movl %eax,4(%edx)
Š Calcular &(sr->a[i])
j
a
leal 8(%edx, %esi, 4),%ecx
Š sr->p = &sr->a[sr->i + sr->j];
p
4 bytes
movl
addl
leal
movl
4(%edx), %eax
(%edx), %eax
8(%edx, %eax, 4), %eax
%eax, 20(%edx)
9
Ejemplo de estructura
struct prob {
Š Calcule el tamaño de la estructura
int *p;
Š Cuáles son los offsets de los campos
struct {
p, s.x, s.y, next?
int x;
Š Código ASM inicialización
int y;
movl 8(%ebp), %eax
} s;
movl 8(%eax), %edx
struct prob *next;
movl %edx, 4(%eax)
};
leal 4(%eax), %edx
Inicializada por
movl %edx, (%eax)
void init(*sp) {
movl %eax, 12(%eax)
sp->s.x = ???
Š Complete ahora el código C anterior
sp->p = ???
sp->next = ???
}
Uniones
Š Permiten referencias de diferentes tipos a un
mismo objeto
union U3 {
char c;
int i[2];
double v;
}
Š Ejemplo ocupa 8 bytes
z
Tamaño de la unión es el tamaño máximo de sus miembros
Ejemplo de union
union ele {
Š
struct {
Š
int *p;
int y;
Š
} e1;
struct {
int x;
union ele *next;
} e2;
};
void proc(union ele *up) {
up->? = *(up->?) – up->?;
Š
}
Calcule el tamaño de la union
Cuáles son los offsets de los campos
e1.p, e1.y, e2.x, e2.next?
Código ASM inicialización
movl
movl
movl
movl
movl
subl
movl
8(%ebp), %eax
4(%eax), %edx
(%edx), %ecx
(%eax), %eax
(%ecx), %ecx
%eax, %ecx
%ecx, 4(%edx)
Complete el código C anterior
Funciones
Š Son la base de la programación estructurada
z
Š Encapsular el código frecuentemente usado en
una función que
z
z
z
z
Funciones
z
Similar a instrucción jmp
Š Pero, es necesario saber adónde debe continuar
la ejecución después de ejecutada la función
Š Es decir, cuál es la dirección de retorno
z
Dirección en memoria de la siguiente instrucción a
ejecutar en la función invocadora
Š Esta dirección se preserva almacenándola en
memoria
©2013 Mario Medina C.
Se escribe y depura una vez
Se invoca muchas veces
Desde diversas partes de un programa
Š Generalmente, tiene
z
Š Una llamada a una función involucra un cambio
en la secuencia de ejecución de instrucciones
Programa es más claro, conciso y mantenible
Un punto de entrada
Un valor de retorno
Instrucciones call y ret
Š Llamada a función en dirección de memoria
rotulo se realiza mediante instrucción
especial call rotulo
z
Instrucción call *operando permite llamar a
una función especificada en forma indirecta
Š Instrucciones almacenan automáticamente la
dirección de retorno en la pila
z
CPU ejecuta código a partir de la dirección
destino
Š Instrucción ret extrae dirección de la pila y
salta a ella
10
Funciones y la pila
Š Funciones utilizan la pila para almacenar la
dirección de retorno
Š La pila puede ser usada también para
z
z
z
z
z
Pasar argumentos a la función invocada
Almacenar variables locales asociadas a la función
invocada
Almacenar variables temporales
Almacenar copias de los registros utilizados en la
función invocada
Retornar un valor a la función invocadora desde la
función invocada
Marco de activación
Š Estructura utilizada para permitar anidar
llamadas a funciones
z
z
Para cada llamada a función, almacena en forma
ordenada los datos ya mencionados
Utiliza el registro %ebp como puntero base al
marco de activación
œQueda fijo durante la ejecución de la función
z
z
z
Registro %esp es el puntero a la pila
Varía según instrucciones pushl y popl
Dirección de retorno queda en 4(%ebp)
Marcos de activación
•
•
•
+4+4n
Argumento n
Marco del
invocador
•
•
•
Direcciones de
memoria
Puntero
a marco
%ebp
Puntero
a pila
%esp
+8
Argumento 1
+4
Dir. de retorno
%ebp guardado
–4
Registros,
variables locales,
y temporales
Marco de la
función en
ejecución
Marcos de activación
Š Marco es área entre registros %ebp (puntero
a marco) y %esp (puntero a pila)
Š Registro %esp (Stack Pointer) apunta a
último dato en la pila
Š Registro %ebp (Base Pointer) sirve de
referencia para el marco de activación
z
Puntero a marco fijo durante ejecución de la
función
œArgumentos en (%ebp + constante)
œVariables locales en (%ebp – constante)
Área de
argumentos
Comienzo de pila
Instrucción leave
Llamada a función
Š Función P llama a función Q
z
z
Argumentos de llamada a Q se almacenan en el
marco de activación de P
Dirección de retorno de función P se almacena en
el marco de activación de P
œÚltimo elemento del marco de activación de P
z
Se almacena el valor actual de %ebp en la pila
œNuevo valor del registro %ebp apunta a esta dirección
de memoria
z
Función Q usa la pila para variables locales, copias
de registros y temporales
©2013 Mario Medina C.
Š Prepara la pila para retorno de función
z
z
Puntero a pila debe apuntar a dirección de
memoria que almacena puntero a marco
Recupera puntero a marco anterior
Š Equivalente a
movl %ebp, %esp
popl %ebp
z
Puede hacerse lo mismo con movl y/o pop
Š Generalmente registro %eax retorna valores
de retorno
11
Convención de uso de registros
Š Registros del procesador compartidos entre
todas las funciones
z
Convención de uso de registros
Š Quién salva los registros?
z
Necesario salvar los registros antes de llamar a una
función
int P(int x) {
int y = x*x;
int z = Q(y);
// y debe existir aqui
return y + z;
}
z
Función invocadora (caller save), o
Función invocada (callee save)?
Š Convención de Intel x86
z
z
Registros %edx, %eax y %ecx salvados por
función invocadora
Registros %ebx, %esi y %edi salvados por
función invocada (si es que los usa!)
Convención de uso de registros
int p(int x) {
int y = x*x;
int z = Q(y);
// y debe
// existir
// aqui
return y + z;
}
Š Cómo salvar el valor de y?
Š Función invocadora puede
z
z
Almacenar y en registro %edx,
%eax o %ecx y guardarlo en la
pila
Almacenar y en registro %esi,
%edi o %ebx y dejar que Q lo
guarde en la pila (sólo si Q
modifica estos registros)
Ejemplo paso de parámetros
int suma(int *xp,
int *yp) {
int x = *xp;
int y = *yp;
}
}
Puntero
a pila
%esp
0
%ebp guardado
–4
arg2
–8
arg1
–12
&arg2
–16
&arg1
%ebp guardado
Marco de
activación
para ejemplo
Puntero
a marco
%ebp
Puntero
a pila
%esp
©2013 Mario Medina C.
Código ejemplo
int ejemplo(void) {
int diff;
int arg1 = 534;
int arg2 = 1057;
En ejecución
de suma
Antes de llamar
a suma
arg2
arg1
+12
int sum = suma(&arg1,
&arg2);
diff = arg1 – arg2;
return sum*diff;
&arg2
+8
&arg1
+4
Dir. retorno
0 %ebp guardado
–4 %ebx guardado
Marco
para
suma
int sum = suma(&arg1,
&arg2);
diff = arg1 – arg2;
return sum*diff;
*xp = y;
*yp = x;
return x + y;
Ejemplo paso de parámetros
Puntero
a marco
%ebp
int ejemplo(void) {
int diff;
int arg1 = 534;
int arg2 = 1057;
}
Š Código ensamblador
leal -4(%ebp), %eax
pushl %eax
leal -8(%ebp), %eax
pushl %eax
call suma
Š Almacena &arg2 en
posición -4(%ebp)
Š Almacena &arg1 en
posición -8(%ebp)
12
Comienzo de suma
suma:
pushl %ebp
// Guarda %ebp
movl %esp, %ebp // Nuevo puntero
pushl %ebx
// Guarda ebx
Š La función suma utiliza el registro %ebx para
almacenar valores temporales
z
Registro %ebx salvado por rutina invocada (callee
save)
Cuerpo de suma
movl
movl
movl
movl
movl
movl
addl
8(%ebp), edx
12(%ebp), %ecx
(%edx), %ebx
(%ecx), %eax
%eax, (%edx)
%ebx, (%ecx)
%ebx, %eax
z
Direcciones referidas a nuevo %ebp
Funciones recursivas
Š Estructuras de pila y marcos de activación
útiles para llamadas recursivas
Š Cada instancia de la función maneja su propio
marco de activación
z
Š Instrucciones en rojo pueden ser reemplazadas
por instrucción leave
Š Valor de retorno de la función está en registro
%eax
Ejemplo: fibonacci()
Š Ojo: no es una implementación eficiente!
int fib_rec(int n)
{
int valorPrev, val;
if (n <= 2)
return 1;
valorPrev = fib_rec(n – 2);
val = fib_rec(n – 1);
return valorPrev + val;
}
©2013 Mario Medina C.
Lee xp
Lee yp
Lee x
Lee y
Guarda y
Guarda x
x + y
Š Código obtiene argumentos de la pila
Final de suma
popl %ebx
// Recupera %ebx
movl %ebp, %esp // Recupera %esp
popl %ebp
// Recupera ebp
ret
// retorno
//
//
//
//
//
//
//
z
z
z
Argumentos
Variables locales
Puntero a la pila
Puntero a marco de activación
Ejemplo: fibonacci en ASM
fib_rec:
pushl %ebp
movl %esp, %ebp
pushl %esi
pushl %ebx
movl 8(%ebp), %ebx
cmpl $2, %ebx
jle .Listo
leal -2(%ebx), %eax
pushl %eax
call fib_rec
//
//
//
//
//
//
//
//
//
//
Guarda %ebp viejo
Define nuevo %ebp
Guarda %esi
Guarda %ebx
Lee n
Compara n y 2
Si <=, ir a Listo:
Calcula n – 2
Graba argumento
fib_rec(n – 2)
13
Ejemplo: fibonacci en ASM
movl %eax, %esi
leal -1(%ebx), %eax
pushl %eax
call fib_rec
addl %esi, %eax
jmp .L1
.Listo: movl $1, %eax
.L1:
popl %ebx
popl %esi
leave
ret
//
//
//
//
//
Resultado en %esi
Calcula n – 1
Graba argumento
fib_rec(n – 1)
valorPrev + val
// Retorna 1
//
//
//
//
Recupera %ebx
Recupera %esi
Recupera %ebp
Retorno
Estado de marco de activación
Puntero
a marco
%ebp
Puntero
a pila
%esp
+8
n
+4
Dir retorno
0
%ebp guardado
–4
%esi guardado
–8
%ebx guardado
Marco de
activación
para
fib_rec(n)
Estado del marco de activación después de
guardar %ebx de fib_rec(n) en la pila
Estado de marco de activación
Convención de paso de argumentos
Š Lenguaje C establece la siguiente convención
para paso de argumentos
n
Dir retorno
%ebp guardado
%esi guardado
z
Marco de
activación
para
fib_rec(n)
%ebx guardado
+8
n-1
Puntero
a marco
%ebp
+4
0
%ebp guardado
Puntero
a pila
%esp
–4
%esi guardado
–8
%ebx guardado
Dir retorno
Marco de
activación
para
fib_rec(n-1)
Estado del marco de
activación después de
guardar %ebx de
fib_rec(n-1) en
la pila
z
Š Lenguaje PASCAL establece otra convención
z
z
Ejemplo
Š Dada función fun(int A, float B, int *C)
push
push
push
call
A
B
C
fun
Š En función fun
ret 12
Š En C
push C
push B
push A
call fun
Add $12, %esp
Š En función fun
ret
©2013 Mario Medina C.
Argumentos se pasan de izquierda a derecha
Función invocada debe ajustar el puntero a la pila
antes de retornar para saltarse los argumentos
Convención de paso de argumentos
Š Convención stdcall es similar a PASCAL, pero
z
Š En PASCAL
Argumentos se pasan de derecha a izquierda
Función invocadora debe ajustar el puntero a la
pila al recuperar el control
z
z
Argumentos se pasan de derecha a izquierda
Función invocada debe ajustar el puntero a la pila
antes de retornar para saltarse los argumentos
Estándar en Win32 API
Š Convención fastcall de Microsoft
z
z
z
Primeros 2 argumentos se pasan en %ecx y %edx
Otros argumentos se pasan de derecha a izquierda
Función invocada debe ajustar el puntero a la pila
antes de retornar para saltarse los argumentos
14
Descargar