Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Ensamblador Gas Ia444222
Ensamblador Gas Ia444222
Indice general
1. Pr
ologo
2. Introducci
on
2.1. Iconos y cajas . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2. Compilando y depurando . . . . . . . . . . . . . . . . . . . .
2.3. Trucos para frikis . . . . . . . . . . . . . . . . . . . . . . . . .
2.3.1. C
omo ver el c
odigo ensamblador de un programa en C
2.3.2. C
omo ver el c
odigo ensamblador de solo una funci
on .
2.3.3. Otra manera de generar ensamblador a partir de C . .
2.3.4. Utilizando el gdb a pelo para debugar . . . . . . . . .
2.4. Los ejemplos de este libro . . . . . . . . . . . . . . . . . . . .
2.5. Otros libros . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.6. Si encuentras alg
un error . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
9
11
13
14
15
15
16
18
18
18
3. Primeros programas
3.1. El IA32 . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.1. La memoria . . . . . . . . . . . . . . . . . . . . . .
3.1.2. Los registros . . . . . . . . . . . . . . . . . . . . .
3.1.3. Tipos de datos, endianismo y alineacion . . . . . .
3.1.4. Modos de direccionamiento y la instruccion mov .
3.2. Mi primer programa . . . . . . . . . . . . . . . . . . . . .
3.3. Copiando datos de un sitio a otro . . . . . . . . . . . . . .
3.4. Operaciones aritmeticas . . . . . . . . . . . . . . . . . . .
3.5. Estructuras de control . . . . . . . . . . . . . . . . . . . .
3.6. Como hago un if/else en ensamblador? . . . . . . . . .
3.7. Como hago un while en ensamblador? . . . . . . . . . .
3.8. Como hago un while(a AND b) en ensamblador? . . . .
3.9. Como hago un while(a OR b) en ensamblador? . . . . .
3.10. Bucles anidados . . . . . . . . . . . . . . . . . . . . . . . .
3.11. Estructura dun programa en IA32 . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
21
21
21
22
24
25
27
29
31
34
36
39
42
44
46
48
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4. Vectores
49
4.1. Declaraci
on y uso de vectores . . . . . . . . . . . . . . . . . . . . 49
4.2. Recorrido de vector de bytes . . . . . . . . . . . . . . . . . . . . . 51
3
INDICE GENERAL
4
4.3.
4.4.
4.5.
4.6.
4.7.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5. Subrutinas
5.1. La pila (stack) . . . . . . . . . . . . .
5.2. Invocar a una subrutina . . . . . . . .
5.3. La diferencia entre call y jmp . . . . .
5.4. Registros a salvar antes de invocar una
5.5. printf y scanf . . . . . . . . . . . . . .
5.6. C
odigo de una subrutina . . . . . . . .
5.7. Salvar registros %ebx, %esi, %edi . . .
5.8. Subrutinas con variables locales . . . .
5.9. Paso de par
ametros por referencia . .
5.9.1. Load Effective Address (lea) . .
5.10. Modos de direccionamiento complejos
5.10.1. OFFSET VAR( %ebp, %ecx,4)
5.10.2. ( %ebx, %ecx,4) . . . . . . . . .
5.10.3. -4( %ebx, %ecx,4) . . . . . . . .
5.10.4. v( %esi, %ecx,4) . . . . . . . . .
6. Eplogo
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
52
53
55
56
59
59
61
63
64
. . . . . .
. . . . . .
. . . . . .
subrutina
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
67
. 67
. 70
. 71
. 72
. 73
. 77
. 81
. 82
. 92
. 96
. 96
. 97
. 100
. 103
. 105
107
Indice de c
odigos fuente
3.1. t5
3.2. t5
3.3. t5
3.4. t5
3.5. t5
3.6. t5
3.7. t5
3.8. t5
3.9. t5
3.10. t5
3.11. t5
4.1. t6
4.2. t6
4.3. t6
4.4. t6
4.5. t6
4.6. t6
4.7. t6
4.8. t6
4.9. t6
4.10. t6
5.1. t7
5.2. t7
5.3. t7
5.4. t7
5.5. t7
5.6. t7
5.7. t7
5.8. t7
5.9. t7
5.10. t7
5.11. t7
5.12. t7
5.13. t7
5.14. t7
p01 add.s . . . . . . . .
p02 inc.s . . . . . . . .
p03 imul.s . . . . . . .
p04 if.s . . . . . . . . .
p05 while.s . . . . . . .
while and.c . . . . . . .
while and.s . . . . . . .
while or.c . . . . . . .
while or.s . . . . . . .
for for.c . . . . . . . .
for for.s . . . . . . . .
vectors.s . . . . . . . .
recorrido a.s . . . . .
recorrido i.s . . . . .
busqueda a.s . . . . . .
busqueda i.s . . . . . .
p03.s . . . . . . . . . .
foob.s . . . . . . . . . .
fool.s . . . . . . . . . .
barb.s . . . . . . . . . .
barl.s . . . . . . . . . .
ex printf.s . . . . . . .
printf.s . . . . . . . .
printf2.s . . . . . . . .
scanf.s . . . . . . . . .
scanf 2.s . . . . . . . .
minusculas print 2.s .
multiplica.c . . . . . .
multiplica.s . . . . . .
signos.s . . . . . . . .
complex swap.c . . . . .
complex swap.s . . . . .
cuento minusculas b.s
cuento minusculasb.s
fibonacci.s . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 27
. 31
. 32
. 36
. 40
. 42
. 43
. 44
. 45
. 46
. 47
. 50
. 51
. 52
. 53
. 55
. 56
. 59
. 61
. 63
. 64
. 70
. 73
. 74
. 75
. 76
. 78
. 83
. 84
. 87
. 92
. 93
. 97
. 100
. 103
INDICE DE CODIGOS
FUENTE
5.15. t7 fibonacci 2.s . . . . . . . . . . . . . . . . . . . . . . . . . . 105
Captulo 1
Pr
ologo
En setiembre del 2012 di por primera vez una asignatura que consiste en
ense
nar a programar en C en ocho semanas y en ensamblador del 386 en seis
semanas a estudiantes de primero de telecomunicaciones. El u
nico problema era
que, si bien soy un buen programador en C, no tena ni remota idea de ensamblador.
-Pero bueno -me dije- tampoco puede ser tan complicado, no?. lol
-Y seguro que hay un buen libro en ingles en el que me puedo basar. lol lol
Al final todo fue muy bien en un cuatrimestre super excitante a la vez que
agotador. Y, con las transpas y apuntes de otros profes, Google y un par de
libros en ingles que no me terminaron de convencer, aprend ensamblador y empece a ense
narlo. X-D
Este libro es el libro que me hubiera gustado tener entonces, y va dedicado
a los alumnos a los que tuve el placer de ense
nar ensamblador por primera vez:
los grupos 70 y 90 del oto
no del 2012.
CAPITULO 1. PROLOGO
Captulo 2
Introducci
on
2.1.
Iconos y cajas
TEORIA
En estas cajas pondremos los conceptos teoricos.
EL CODIGO
En estas cajas explicaremos las lineas de c
odigo mas importantes.
TRAMPAS
9
CAPITULO 2. INTRODUCCION
10
TRUCOS
En estas cajas daremos trucos varios.
EL BOXEO
Million Dollar Baby (2004):
El boxeo es un acto antinatural porque todo va al reves. Si
quieres desplazarte hacia la izquierda, no das un paso a la izquierda,
cargas sobre el pie derecho. Para desplazarte hacia la derecha usas
el pie izquierdo. En vez de huir del dolor como hara una persona
cuerda, das un paso hacia el. En el boxeo todo va al reves. - Eddie
Scrap-Iron Dupris (interpretado por Morgan Freeman)
Al igual que en el boxeo, cuando empecemos a programar en ensamblador puede que tambien nos parezca que todo va al reves. Y que es
imposible de aprender. Pero al final de este libro ya todo nos parecer
a normal.
En estas cajas utilizaremos la percepcion de que el ensamblador es
como el boxeo porque todo va al reves como mnemotecnico.
foo . s
El $ no lo escribes t
u. Se llama prompt en ingles y aparece solo. En este
ejemplo, el usuario teclea el comando ls que hace que el sistema operativo
11
muestre por pantalla los ficheros existentes en el directorio actual (foo y foo.s).
Si no sabes lo que es una consola, consulta:
http://valentux.wordpress.com/2010/01/26/la-consola-o-terminal-en-gnulinux-lobasico/
2.2.
Compilando y depurando
12
CAPITULO 2. INTRODUCCION
13
modo texto estilo vi. Ubuntu tiene el paquete cgdb. Si no usas el vi de forma
habitual ni lo intentes, no tienes el suficiente nivel friki para esto. M
as informacion sobre cgdb en:
http://cgdb.github.io/
Editando el c
odigo fuente: Si usais Ubuntu yo recomiendo que utiliceis
gedit o vuestro editor preferido. Para editar el c
odigo fuente foo.s
$ gedit foo . s &
$
Yo utilizo el vim o el gvim, pero es un editor muy poco intuitivo y se tarda
mucho en aprender a utilizarlo.
Sintaxis utilizada en este libro: El c
odigo ensamblador del IA32 (Arquitectura Intel de 32 bits, tambien conocida como i386 porque se uso por primera
vez en el Intel 386) se puede escribir utilizando dos sintaxis diferentes: la Intel y
la AT&T. En este libro utilizaremos la sintaxis AT&T. Tenedlo presente cuando
mireis otros libros o ejemplos de c
odigos en ensamblador.
TRUCOS
Es sintaxis AT&T si en el c
odigo aparecen $ y % y probablemente
tambien aparecen (, ).
Es sintaxis Intel si en el c
odigo no aparecen $ y % y probablemente si que aparecen [ , ].
2.3.
CAPITULO 2. INTRODUCCION
14
2.3.1.
C
omo ver el c
odigo ensamblador de un programa
en C
$ cat f o o . c
i n t a =0;
main ( ) {
a = a + 2;
}
$ g c c g c f o o . c
$ LANG=C objdump d M s u f f i x f o o . o
foo . o :
f i l e fo r ma t e l f 3 2 i 3 8 6
Disassembly o f s e c t i o n . text :
00000000 <main >:
0:
55
pushl
1:
89 e5
movl
3:
a1 00 00 00 00
movl
8:
83 c0 02
a ddl
b:
a3 00 00 00 00
movl
10:
5d
po pl
11:
c3
retl
$ LANG=C objdump S M s u f f i x f o o . o
foo . o :
%ebp
%esp , %ebp
0x0, %eax
$0x2, %eax
%eax , 0 x0
%ebp
f i l e fo r ma t e l f 3 2 i 3 8 6
Disassembly o f s e c t i o n . text :
00000000 <main >:
i n t a =0;
main ( ) {
0:
1:
a =
3:
8:
b:
}
10:
11:
55
89 e5
a + 2;
a1 00 00 00 00
83 c0 02
a3 00 00 00 00
pushl
movl
%ebp
%esp , %ebp
movl
a ddl
movl
0x0, %eax
$0x2, %eax
%eax , 0 x0
5d
c3
po pl
retl
%ebp
15
$
El LANG=C es para obligarle a mostrarme la salida en ingles.
2.3.2.
C
omo ver el c
odigo ensamblador de s
olo una funci
on
$ cat f o o . c
i n t a =0;
main ( ) {
a = a + 2;
}
$ g c c g o f o o f o o . c
$ gdb f o o
GNU gdb (GDB) 7 . 5 . 9 1 . 2 0 1 3 0 4 1 7 cvsubuntu
[...]
( gdb ) d i s a s s e m b l e main
Dump o f a s s e m b l e r co de for function main :
0 x0 8 0 4 8 3 ec <+0>: push
%ebp
0 x080483ed <+1>: mov
%esp , %ebp
0 x 0 8 0 4 8 3 e f <+3>: mov
0 x804a020 , %eax
0 x 0 8 0 4 8 3 f 4 <+8>: add
$0x2, %eax
0 x 0 8 0 4 8 3 f 7 <+11>:
mov
%eax , 0 x804a020
0 x 0 8 0 4 8 3 f c <+16>:
pop
%ebp
0 x0 8 0 4 8 3 fd <+17>:
ret
End o f a s s e m b l e r dump .
( gdb ) q u i t
$
2.3.3.
$ cat f o o . c
i n t a =0;
main ( ) {
a = a + 2;
}
$ g c c S f o o . c
$ cat f o o . s
. file
foo . c
. globl a
. bss
. align 4
. type
a , @ o bject
CAPITULO 2. INTRODUCCION
16
. size
a, 4
a:
. zero
4
. text
. g l o b l main
. type
main , @ f u n c t i o n
main :
. LFB0 :
. cfi startproc
pushl
%ebp
. cfi def cfa offset 8
. c f i o f f s e t 5 , 8
movl
%esp , %ebp
. cfi def cfa register 5
movl
a , %eax
a ddl
$2 , %eax
movl
%eax , a
po pl
%ebp
. cfi restore 5
. cfi def cfa 4, 4
ret
. cfi endproc
. LFE0 :
. size
main , .main
. i d e n t GCC: ( Ubuntu/ L i n a r o 4 .7 .3 1 ubuntu1 ) 4 . 7 . 3
. section
. no te .GNUs t a c k , , @ p r o g b i t s
$
2.3.4.
$ cat f l a g s . s
. bss
. comm a , 4 , 1
. comm b , 4 , 1
. text
. g l o b a l main
main :
movb $1 , a
movb $ 1 , b
movb a , %a l
subb b , %a l
movl $0 , %ebx
movl $1 , %eax
i n t $0x80
$ g c c g o f l a g s f l a g s . s
17
CAPITULO 2. INTRODUCCION
18
2.4.
El c
odigo fuente de todos los ejemplos de este libro, al igual que el libro en
formato PDF, se pueden descargar de mi p
agina de la asignatura:
http://people.ac.upc.edu/guerrero/fo.html
En la p
agina teneis el PDF del libro y los ejemplos del libro en un fichero comprimido llamado asm src.tgz. Os bajais el fichero comprimido a un directorio.
Y para descomprimir el fichero haceis:
$ cd < n o m b r e d e l d i r e c t o r i o d o n d e e s t a e l f i c h e r o t g z >
$ tar xzvf asm src . tgz
[...]
$
Esto os crear
a un subdirectorio llamado asm scr que contendra todos los ficheros
de ejemplo.
Vereis que empiezan por t5, t6 y t7. Eso, como ya habreis intuido es por que
corresponden a los temas 5, 6 y 7 de la asignatura.
En la p
agina de la asignatura hay versiones de estos mismos ejemplos pero
pueden ser ligeramente diferentes.
2.5.
Otros libros
2.6.
Si encuentras alg
un error
ERROR
2.6. SI ENCUENTRAS ALGUN
19
Si encuentras alg
un error (ya sea en los programas, faltas de ortografa,
etcetera) o tienes alguna idea de c
omo mejorar este libro y quieres enviarme
un e-mail, puedes hacerlo a: guerrero arroba ac punto upc punto edu. Haz que
el subject (asunto?) del e-mail empiece por [Libro IA32]. Decidme cual es la
fecha que aparece en la portada para que sepa a que versi
on os refers. Muchas
gracias. :-)
20
CAPITULO 2. INTRODUCCION
Captulo 3
Primeros programas
3.1.
El IA32
En esta secci
on voy a describir solo lo imprescindible del IA32 para poder
programar en ensamblador.
Ver
as que esta primera secci
on es super densa y puede que no termines
de entenderla del todo. Pero con los ejemplos de las siguientes secciones ya te
ir
a quedando todo mas claro.
3.1.1.
La memoria
22
n
0
1
2
3
4
5
6
7
bin
000
001
010
011
100
101
110
111
n
3
2
1
0
-0
-1
-2
-3
Ca1
011
010
001
000
111
110
101
100
n
3
2
1
0
-1
-2
-3
-4
Ca2
011
010
001
000
111
110
101
100
TRUCOS
Conversi
on rapida a Ca2 para humanos:
#1: empezando por la derecha copiar el n
umero original hasta el
primer 1 (primer 1 incluido).
#2: Despues, se niegan (complementan) los dgitos restantes (es
decir, se substituye 0 por 1 y viceversa).
Ej: 92 -> -92
#1 01011100 -> ?????100
#2 01011100 -> 10100100
3.1.2.
Los registros
A parte de la memoria, el IA32 puede almacenar datos en lo que se denominan registros. Cada registro puede almacenar un n
umero binario de 32 bits. El
acceso (lectura o escritura) a un registro es muchsimo mas rapido que el acceso
a memoria.
Existen 8 registros denominados de prop
osito general:
EAX (Accumulator). Para operaciones aritmeticas.
ECX (Counter). Contador para bucles (como la variable i en C).
3.1. EL IA32
23
31..16
31..24
23..16
15..00
AX
BX
DX
CX
15..08
AH
BH
DH
CH
07..00
AL
BL
DL
CL
Los dos u
ltimos registros de prop
osito general (ESP y EBP) ya veremos
exactamente para que sirven cuando estudiemos llamadas a subrutinas (una
subrutina es el equivalente de una funci
on en C). Los 6 primeros los podemos
utilizar para lo que queramos pero cuando los he listado os he indicado para
que suelen usarse.
24
3.1.3.
3.1. EL IA32
3.1.4.
25
Una instrucci
on tpicamente trabaja sobre uno o mas operadores. Por ejemplo la instrucci
on mov copia el valor de un primer operador a un segundo
operador (que sera un registro o una posicion de memoria).
El formato tpico de una instruccion es:
INSTRUCCION OPERADOR1 OPERADOR2
Donde al operador2 tambien se lo conoce como operador de destino
Tambien tenemos instrucciones que trabajan con un u
nico operador:
INSTRUCCION OPERADOR1
Los modos de direccionamiento sirven para indicar donde est
an los operadores de una instrucci
on. Tenemos los siguientes:
1. Inmediato: $100 (el n
umero 100) (Trampa: el operador de destino
nunca puede ser inmediato.)
2. Registro: %eax (el registro eax)
3. Memoria.
Ejemplos de mov: Asignar un 0 al registro eax:
1
movl $0 , %eax
Copiar el valor del registro eax al registro ebx:
movb $ A , %a l
Fjate que mov, al igual que todas las instrucciones que operan con datos
termina con l, w o b dependiendo de si opera con longs, words o bytes. Y
si bien es cierto que compilar
a sin problemas en la mayora de los casos aunque
no le a
nadamos la l, w o b, lo mejor es que nos acostumbremos a ponerla
siempre.
En cuanto al modo de direccionamiento a memoria tenemos:
3.a. Memoria, Normal: ( %eax) = valor en la posicion de memoria contenida en el registro eax. Ejemplo: movl ( %ecx), %eax lee el long contenido
en la posicion de memoria contenida en el registro ecx y la copia al registro
eax.
26
TRAMPAS
Solamente 1 operando puede estar en memoria.
movl ( %eax), ( %ebx) dar
a error de compilaci
on!
movl a, b dar
a error de compilaci
on!
Esto sucede en todas las instrucciones! No solo en mov.
27
EL BOXEO
El ensamblador es como el boxeo porque todo va al reves:
El mov es el equivalente del = en C. En C asignamos el valor de
la derecha a la variable de la izquierda a=0;. En ensamblador con
sintaxis AT&T asignamos el valor de la izquierda (operador 1) a la
variable o registro de la derecha (operador 2) movl $0, %eax;
C
1
&1
i
&i
eax
*eax
Descripcion
El n
umero 1
La direcci
on de memoria 1
El valor de la variable i
Posicion de memoria donde est
a la variable i
Valor guardado en el registro eax.
Valor guardado en la pos de memoria en eax
3.2.
Mi primer programa
C
odigo fuente 3.1: t5 p01 add.s
1 #===[E q u i v a l e n c i a en C]============
2 # i nt a=2 , b=3 , r ;
3 # r = a + b;
4
5 .data
# i n i t i a l i z e d g l o b a l da ta
6 a : . l o n g 2 # i nt a = 2 ;
7 b : . l o n g 3 # i nt b = 3 ;
8
9 .bss
# u n i n i t i a l i z e d g l o b a l da ta
10 .comm r , 4 , 4 # i nt r ;
28
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.text
# code segment
. g l o b a l main
# So t h e OS can c a l l main
main :
# main ( ) {
movl a , %eax
# a > eax
a ddl b , %eax
# b + eax > eax
movl %eax , r
# eax > r
# LINUX EXIT
#movl $0 , %ebx # Return 0 ( t h e u s u a l )
movl r , %ebx
# Return r ( l o w e s t byte )
movl $1 , %eax
i nt $0x80
$ g c c g o t 5 p 0 1 a d d t 5 p 0 1 a d d . s
$ . / t5 p01 add
$ echo $ ?
5
$
EL CODIGO
Lneas 1-3: Este es nuestro primer programa que suma 2+3. Aqu tenemos el c
odigo equivalente en C. El # indica que el resto de la lnea
es un comentario.
Lneas 5-7: El bloque data contiene las variables que declaramos e
inicializamos. En este caso dos longs (a=2 y b=3).
Lnea 9-10: El bloque bss contiene las variables que declaramos pero
no inicializamos. En este caso la variable r de 4 bytes (long) alineada
a 4 bytes.
Lnea 12: El bloque text contiene las instrucciones del programa
Lnea 13: Un programa siempre empieza haciendo publica la etiqueta
main para que pueda ser invocada por el Sistema Operativo.
Lnea 15: La etiqueta main, donde empieza el programa.
Lnea 16: Copiamos a al registro eax.
29
Lnea 17: Sumamos (add significa sumar en ingles) b con el contenido del registro eax y el resultado lo guardamos en eax. Ahora
eax contiene a+b.
Lnea 18: Copiamos el contenido de eax a la variable r.
Lnea 20: El programa ya ha acabado y ahora debemos retornar el
control al Sistema Operativo. En Linux se hace de la siguiente manera:
Lnea 21: Normalmente se copia un 0 al registro ebx. Aqu est
a comentado.
Lnea 22: Copiamos el resultado de la ejecuci
on (la variable r) al
registro ebx.
Lnea 23: Copiamos un 1 al registro eax.
Lnea 24: Llamamos a la interrupcion 0x80 (80 hexadecimal). Esta
interrupci
on lo que hace es (en el caso de que el registro eax valga
1 termina la ejecuci
on del programa devolviendo el contenido del
registro ebx al sistema operativo.
TRUCOS
Lo compilamos con el gcc y lo ejecutamos. Justo despues de ejecutarlo
tecleamos echo $?, que hace que el Sistema Operativo nos muestre
que es lo que le ha devuelto el u
ltimo programa ejecutado modulo
256. Recordad que cuando un programa termina su ejecuci
on pasa el
contenido de ebx (la parte bl para ser exactos) al Sistema Operativo.
Este peque
no truco nos permitira ver que los programas funcionan
correctamente sin necesidad de un depurador hasta que aprendamos
a hacer printf.
Este truco lo saque del libro: Programming from the Ground Up
(por Jonathan Bartlett).
3.3.
30
que ser copiar).
1
1
2
movl $ 1 , %a l
movsbl %al , %ebx
Usamos movs en lugar de movz ya que -1 es 0xFF en hexadecimal y
queremos que ebx valga -1 (0xFFFFFFFF) y no 0x000000FF (+255).
3.4. OPERACIONES ARITMETICAS
3.4.
31
Operaciones aritm
eticas
Instrucci
on
add[bwl] op1, op2
sub[bwl] op1, op2
inc[bwl] op1
dec[bwl] op1
neg[bwl] op1
imul[bwl] op1, op2
Operaci
on
op2 = op2 + op1
op2 = op2 - op1
op1++
op1
op1 = -op1
op2 = op1 * op2 (op2 registro)
Aqu vemos las instrucciones que podemos utilizar par sumar, restar, incrementar, decrementar, negar y para hacer multiplicaciones enteras. Es bastante
intuitivo. Recordad que en la multiplicacion entera el segundo operando tiene
que ser un registro (no puede ser una posicion de memoria). Los nombres vienen
del ingles add, subtract, increment, decrement, negate, e integer multiplication
(sumar, restar, incrementar, decrementar, negar y multiplicacion entera).
TRAMPAS
En imul el operador 2 tiene que ser un registro!
No puede ser ninguna variable ni posicion de memoria.
imull %eax, var dar
a error de compilaci
on!
Es imull, imulw e imulb. No imul, imuw e imub !
#===[E q u i v a l e n c i a en C]============
# i nt i =0 , j =3 , r ;
# i++; j ;
# j = j ;
# r = i j;
.data
i : .long 0
j : .long 3
# i n i t i a l i z e d g l o b a l da ta
# i nt i = 0 ;
# i nt j = 3 ;
.bss
# u n i n i t i a l i z e d g l o b a l da ta
.comm r , 4 , 4 # i nt r ;
32
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
.text
# code segment
. g l o b a l main
main :
incl i
decl j
negl j
#
#
#
#
main ( ) {
i++
j
j = j
movl i , %eax
s u b l j , %eax
# i > eax
# i j > eax
movl %eax , r
# i j > r
# LINUX EXIT
#movl $0 , %ebx # Return 0 ( t h e u s u a l )
movl r , %ebx
# Return r ( i t s l o w e s t byte )
movl $1 , %eax
i nt $0x80
$ g c c g o t 5 p 0 2 i n c t 5 p 0 2 i n c . s
$ ./ t5 p02 inc
$ echo $ ?
3
$
Codigo fuente 3.3: t5 p03 imul.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#===[E q u i v a l e n c i a en C]============
# #d e f i n e N = 10
# i nt i =2 , j =3 , k=6 , r ;
# r = ( i + j ) ( k + ( 2) ) + N;
N = 10
# d e f i n e N 10
.data
i : .long 2
j : .long 3
k : .long 4
#
#
#
#
i n i t i a l i z e d g l o b a l da ta
i nt i = 2 ;
i nt j = 3 ;
i nt k = 4 ;
.bss
# u n i n i t i a l i z e d g l o b a l da ta
.comm r , 4 , 4 # i nt r ;
.text
# code segment
. g l o b a l main
# So t h e OS can c a l l main
3.4. OPERACIONES ARITMETICAS
33
19 main :
# main ( ) {
20
movl i , %eax
# i > eax
21
a ddl j , %eax
# i+j > eax
22
movl %eax , r
# i+j > r
23
24
movl k , %eax
# k > eax
25
a ddl $ 2 , %eax # k + ( 2) > eax
26
i m u l l r , %eax # ( i+j ) ( k+(2) ) > eax
27 # El operando d e s t i n o de imul t i e n e que s e r r e g i s t r o
28
a ddl $N , %eax # ( i+j ) ( k+(2) ) + N > eax
29
30
movl %eax , r
# ( i+j ) ( k+(2) ) + N > r
31
32 # LINUX EXIT
33 #movl $0 , %ebx # Return 0 ( t h e u s u a l )
34
movl r , %ebx
# Return r ( i t s l o w e s t byte )
35
movl $1 , %eax
36
i nt $0x80
$ g c c g o t 5 p 0 3 i m u l t 5 p 0 3 i m u l . s
$ . / t 5 p 0 3 i m u l ; echo $ ?
20
$
EL BOXEO
El ensamblador es como el boxeo porque todo va al reves:
add es equivalente a += en C.
(Por si no lo sabeis, en C, a += 2; equivale a a = a+2;. A
que mola! Lo mismo con -=, *=, /=, %=. Por lo tanto add
equivale a +=, sub equivale a -= e imul equivale a *=.)
Pero, como el ensamblador va al reves, si en C asignamos el
resultado de la operaci
on a la variable de la izquierda a+=2;.
En ensamblador con sintaxis AT&T asignamos el resultado de la
operaci
on a la variable o registro de la derecha (operador 2) addl
$2, %eax;
En todas las operaciones aritmeticas con dos operadores el resultado
de la operaci
on se guarda en el operador 2 (el operador de la derecha).
34
TRAMPAS
Recordad: En cualquier instruccion, solamente uno de los operandos
puede estar en memoria (ser una variable o una direcci
on de memoria).
addl a, b dar
a error de compilaci
on!
addl ( %eax), b dar
a error de compilaci
on!
3.5.
Estructuras de control
Instrucci
on
cmp[bwl] op1, op2
Operaci
on
op2 - op1 (op2 no puede ser immediato!)
Los saltos incondicionales se hacen con jmp etiq. No usan cmp antes. El
ordenador al llegar a ejecutar esta instruccion lo que hace es saltar a la etiqueta
etiq y seguir la ejecuci
on desde all.
Aqu teneis una tabla con las instrucciones de salto:
Operaci
on
op2 - op1
Salta a etiq
Salta si op2 == op1
Salta si op2 != op1
Salta si op2 > op1
Salta si op2 op1
Salta si op2 < op1
Salta si op2 op1
Invoca la subrutina etiq
35
Flags
ZF, SF i OF
ZF == 1
ZF == 0
ZF=0 y SF=OF
SF=OF
SF!=OF
ZF==1 o SF!=OF
Los nombres vienen del ingles: cmp compare (comparar), jmp jump (saltar),
je jump if equal (saltar si son iguales), jne jump if not equal (saltar si no son
iguales), jg jump if greater (saltar si es mayor), jge jump if greater or equal
(saltar si es mayor o igual), jl jump if less (saltar si es menor), jle jump if less
or equal (saltar si es menor o igual),
Finalmente para invocar una subrutina, utilizaremos call (llamar).
TRAMPAS
En cmp el operador no puede ser inmediato!
(Inmediato significa una constante)
cmpl %eax, $0 dar
a error de compilaci
on!
Teneis que hacer cmpl $0, %eax.
EL BOXEO
El ensamblador es como el boxeo porque todo va al reves:
En la sintaxis AT&T se compara el operador 2 con el operador 1. Por
ejemplo cmpl op1, op2 seguido de jg etiq salta si op2 es mas grande
que op1. Cuando lo intuitivo sera que saltara si op1 fuera mas grande
que op2.
36
3.6.
1
2
3
4
5
C
omo hago un if/else en ensamblador?
i f ( a == 0 ) {
r = 1;
} else {
r = 1;
}
Para hacer un if en ensamblador va bien pensar el c
odigo en C y luego:
1. hacer un cmp de los dos operandos que comparamos pero al reves. Esto
es porque en la sintaxis AT&T se compara el segundo operador con el
primero. As que haramos: lnea 1 cmpl $0, a. En C si uno de los dos
operandos es una constante se suele poner a la derecha. Lo que nos va
perfecto porque recordad que el segundo operador no puede ser inmediato
(una constante).
2. Hacer el salto condicional con la condicion para ir al else. Si la condicion
del if es si son iguales la condicion para ir al else es si no son iguales.
Por lo tanto: lnea 2 jne else.
3. Poner el bloque then: lnea 3 movl $1, r. (En C, el bloque then es el
c
odigo que se ejecuta si la condicion es cierta)
4. Despues del bloque then salto incondicional al final del if: lnea 4 jmp
endif.
5. Etiqueta del else: lnea 5 else:.
6. El bloque de c
odigo else: lnea 6: movl $-1, r. (En C, el bloque else es
el c
odigo que se ejecuta si la condicion es falsa)
7. Etiqueta de fin de if: lnea 7: endif:
1
2
3
4
5
6
7
cmpl $0 , a
jne else
movl $1 , r
jmp e n d i f
else :
movl $ 1 , r
endif :
Aqu tenemos un ejemplo completo de un programa simple con un if:
Codigo fuente 3.4: t5 p04 if.s
1 #===[E q u i v a l e n c i a en C]============
2 # i nt a =3 , b=3 , r ;
3 # i f ( a >= b ) r=1 ; e l s e r =0;
3.6. COMO
HAGO UN IF/ELSE EN ENSAMBLADOR?
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
.data
a : .long 3
b: .long 3
37
# i n i t i a l i z e d g l o b a l da ta
# i nt a = 3 ;
# i nt b = 3 ;
.bss
# u n i n i t i a l i z e d g l o b a l da ta
.comm r , 4 , 4 # i nt r ;
.text
# code segment
. g l o b a l main
main :
#
movl a , %eax
cmpl b , %eax
j l else
#
movl $1 , r #
jmp endif #
else :
#
movl $0 , r #
endif :
#
main ( ) {
# i f ( a >= b )
{
r=1 ;
}
else {
r=0 ;
}
# LINUX EXIT
#movl $0 , %ebx # Return 0 ( t h e u s u a l )
movl r , %ebx
# Return r ( i t s l o w e s t byte )
movl $1 , %eax
i nt $0x80
$ g c c g o t 5 p 0 4 i f t 5 p 0 4 i f . s
$ . / t 5 p 0 4 i f ; echo $ ?
1
$
EL CODIGO
Paso 1) Lnea 16-17: Atentos a la trampa! No podemos hacer un cmpl
b, a. Porque? Por que en ensamblador del IA32 los dos operandos no
pueden estar en memoria. (Si, las variables se guardan en la memoria).
Por lo tanto, primero moveremos a a un registro y luego compararemos b con ese registro. Fijaros que intercambio los operadores de
C a ensamblador.
38
TRAMPAS
El salto condicional con la condicion opuesta al salto jge es jl, no
jle.
El salto condicional con la condicion opuesta al salto jg es jle, no
jl.
No puede haber dos etiquetas iguales en un programa. Si haceis otro
if utilizad etiquetas diferentes. Como por ejemplo: else1, endif1 y
else2, endif2.
No podemos hacer un cmp con dos variables o con dos operandos
que esten en memoria. De hecho ninguna instruccion puede tener sus
dos operandos en memoria. La solucion es mover uno de ellos a un
registro y comparar la variable con el registro.
EL BOXEO
El ensamblador es como el boxeo porque todo va al reves:
En un if los operandos de la comparaci
on est
an del reves y el salto
3.7. COMO
HAGO UN WHILE EN ENSAMBLADOR?
39
3.7.
1
2
3
4
5
C
omo hago un while en ensamblador?
i = 1;
while ( i <= 1 0 ) {
r = r+i ;
i ++;
}
Para hacer un bucle en ensamblador va bien pensar primero el c
odigo en C.
En ensamblador a un bucle le llamaremos loop. Es mas inmediato traducir un
while a ensamblador que un for. Cuando ya tenemos el while en C:
1. La inicializacion de la variable contador ya la sabemos hacer: lnea 1 movl
$1, %ecx.
2. Empieza el bucle. Necesitaremos una etiqueta aqu: lnea 2 loop: (while:
tambien estara bien).
3. hacer un cmp de los dos operandos que comparamos en la condicion
del while en C pero al reves. Esto es porque en la sintaxis AT&T se
compara el segundo operador con el primero. As que haramos: lnea 3
cmpl $10, %ecx. Acordaros, que al igual que en el caso del if en C si
uno de los dos operandos es una constante se suele poner a la derecha. Lo
que nos va perfecto porque en ensamblador el segundo operador de una
instrucci
on no puede ser inmediato (una constante).
4. Hacer el salto condicional al final del bucle con la condicion de salida del
bucle (que es la opuesta a la condicion de permanencia que aparece en el
while en C). Si la condicion de permanencia del while era i menor o igual
que 10 en ensamblador la condicion de finalizacion del loop sera %exc
mayor que 10. Por lo tanto: lnea 4 jg endloop.
5. Poner el cuerpo del bucle: lnea 5 addl %ecx, r.
6. Despues del cuerpo del bucle incremento el contador del bucle: lnea 6
incl %ecx.
7. Salto incondicional al principio del bucle: lnea 7 jmp loop.
8. Etiqueta de fin de bucle: lnea 8: endloop:
40
1
2
3
4
5
6
7
8
movl $1 , %ecx
loop :
cmpl $10 , %ecx
j g endlo o p
a ddl %ecx , r
i n c l %ecx
jmp l o o p
endlo o p :
Aqu tenemos un ejemplo completo de un programa simple con un while:
Codigo fuente 3.5: t5 p05 while.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#===[E q u i v a l e n c i a en C]============
# i nt f =1 , n=3 , i ;
# i =1 ; w h i l e ( i<=n ) { f=f i ; i ++;}
# r e t o r n a r f ; // f < n f a c t o r i a l
.data
f : .long 1
n: .long 3
# i n i t i a l i z e d g l o b a l da ta
# i nt f = 1 ;
# i nt n = 3 ;
.text
# code segment
. g l o b a l main
main :
# main ( ) {
movl f , %eax
movl $1 , %ecx
# i = 1 ; // e c x = i
while :
# w h i l e ( i <=n )
cmpl n , %ecx
jg e n d w h i l e
i m u l l %ecx , %eax
# f=f i ; # op2 r e g i s t r o !
i n c l %ecx
# i++;
jmp w h i l e
endwhile :
movl %eax , f
# LINUX EXIT
movl f , %ebx
movl $1 , %eax
i nt $0x80
# Return f ( i t s l o w e s t byte )
$ g c c g o t 5 p 0 5 w h i l e t 5 p 0 5 w h i l e . s
$ . / t 5 p 0 5 w h i l e ; echo $ ?
6
$
3.7. COMO
HAGO UN WHILE EN ENSAMBLADOR?
41
EL CODIGO
Paso 1) Lnea 15: De la misma manera que en C se suele usar la
variable i como contador, en ensamblador se suele usar el registro
%ecx. Lo inicializamos a cero.
Paso 2) Lnea 16: La etiqueta while a la que saltaremos en cada
iteraci
on.
Paso 3) Lnea 17: La comparaci
on con los operandos al reves que en
C.
Paso 4) Lnea 18: El salto condicional con la condicion de salida del
bucle a la etiqueta de final de bucle. Si la condicion de permanencia
del while era que i sea menor que n la de salida sera que %ecx sea
mayor que n.
Paso 5) Lnea 19: El cuerpo del bucle.
Paso 6) Lnea 20: Incremento del contador del bucle.
Paso 7) Lnea 21: Salto incondicional al principio del bucle.
Paso 8) Lnea 22: Etiqueta de final de bucle.
TRAMPAS
Recordad las trampas de if/else que aqu tambien est
an.
Peque
na trampa: El segundo operando de imul tiene que ser registro.
Por eso en la linea 14 movemos f a registro.
EL BOXEO
42
3.8.
C
omo hago un while(a AND b) en ensamblador?
g c c g o t 5 w h i l e a n d t 5 w h i l e a n d . c
./ t5 while and c
= 1, n = 2
= 2, n = 4
= 3, n = 8
= 4 , n = 16
= 5 , n = 32
= 6 , n = 64
= 7 , n = 128
3.8. COMO
HAGO UN WHILE(A AND B) EN ENSAMBLADOR?
43
$
A continuacion vemos el mismo c
odigo pero ahora en ensamblador. Como
a
un no sabemos hacer printf, solo retornaremos la cantidad de granos de la
primera casilla que contiene 100 o mas granos.
C
odigo fuente 3.7: t5 while and.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#===[C e q u i v a l e n t e]============
# i nt n=1 , i =0 ;
# w h i l e ( n<100 && i <64) {n=n2 ; i ++;}
# retornar n ;
.data
n : .long 1
i : .long 0
# i n i t i a l i z e d g l o b a l da ta
# i nt n = 1 ;
# i nt i = 0 ;
.text
# code segment
. g l o b a l main
main :
movl n , %eax
movl i , %ecx
while :
cmpl $100 , %eax
jge e n d w h i l e
# %eax>=100
cmpl $64 , %ecx
jge e n d w h i l e
# %ecx>=10
i m u l l $2 , %eax
i n c l %ecx
jmp w h i l e
endwhile :
movl %eax , n
# LINUX EXIT
movl n , %ebx # Retorna n %256 !
movl $1 , %eax
i nt $0x80
$ g c c g o t 5 w h i l e a n d t 5 w h i l e a n d . s
$ . / t 5 w h i l e a n d ; echo $ ?
128
$
44
EL CODIGO
Lneas 17-20: El concepto es simple. Para implementar while (a AND
b) lo que haremos es, justo despues de la etiqueta while: si a no se
cumple saltar al final del bucle y, luego, si b no se cumple saltar al
final del bucle.
Lnea 21: Despues de esto viene el cuerpo del bucle (al que solo iremos
a parar si tanto a como b se cumplan).
3.9.
C
omo hago un while(a OR b) en ensamblador?
Este caso es un poco mas complejo. Cojamos este ejemplo en C (que no hace
nada u
til):
Codigo fuente 3.8: t5 while or.c
1 #include<s t d i o . h>
2 main ( ) {
3
i nt n=7 , m=1;
4
while ( n<100 | | m<100) {
5
n=n 2 ;
6
m=m 3 ;
7
p r i n t f ( n= %d\tm= %d\n , n ,m) ;
8
}
9
p r i n t f ( Reto r na r n= %d\n , n ) ;
10 }
11 /
12 n=14
m=3
13 n=28
m=9
14 n=56
m=27
15 n=112
m=81
16 n=224
m=243
17 R et orn ar n=224
18 /
$ g c c g o t 5 w h i l e o r t 5 w h i l e o r . c
$ ./ t5 while or
n=14
m=3
3.9. COMO
HAGO UN WHILE(A OR B) EN ENSAMBLADOR?
n=28
m=9
n=56
m=27
n=112
m=81
n=224
m=243
Reto r na r n=224
$
El c
odigo traducido al ensamblador sera el siguiente:
C
odigo fuente 3.9: t5 while or.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#===[C e q u i v a l e n t e]============
# i nt n=7 ; m=1;
# w h i l e ( n < 1 0 0 | |m<100) {n=n2 ; m=m 3 ; }
# retornar n ;
.data
n : .long 7
m: . l o n g 1
# i n i t i a l i z e d g l o b a l da ta
# i nt n = 7 ;
# i nt m = 1 ;
.text
# code segment
. g l o b a l main
main :
movl n , %eax
movl m, %ebx
while :
# while
cmpl $100 , %eax
j l ok
# %eax<100
cmpl $100 , %ebx
jge e n d w h i l e
# %ebx>=100
ok :
# Cuerpo d e l b u c l e
i m u l l $2 , %eax
i m u l l $3 , %ebx
jmp w h i l e
endwhile :
movl %eax , n
# LINUX EXIT
movl n , %ebx
movl $1 , %eax
i nt $0x80
$ g c c g o t 5 w h i l e o r t 5 w h i l e o r . s
$ . / t 5 w h i l e o r ; echo $ ?
224
$
45
46
EL CODIGO
Lneas 17-18: Para implementar while (a OR b) lo que haremos es,
justo despues de la etiqueta while: si a es cierto a OR b es cierto
y, por lo tanto, saltamos al cuerpo del bucle (ok:).
Lneas 19-20: Si a era falso, si no b es falso a OR b es falso y por lo
tanto saltamos al final del bucle, de lo contrario iremos a la siguiente
lnea que es el principio del cuerpo del bucle.
3.10.
Bucles anidados
Ya para acabar, vamos a ver un ejemplo con un bucle dentro de otro bucle:
Codigo fuente 3.10: t5 for for.c
1 #include <s t d i o . h>
2 main ( ) {
3
i nt i , j , n=0;
4
for ( i =1; i <5; i ++) {
5
for ( j=i ; j <5; j ++) {
6
n++;
7
p r i n t f ( i= %d , j= %d , n= %d\n , i , j , n ) ;
8
}
9
}
10 }
$ g c c g o t 5 f o r f o r t 5 f o r f o r . c
$ ./ t 5 f or for
i =1 , j =1 , n=1
i =1 , j =2 , n=2
i =1 , j =3 , n=3
i =1 , j =4 , n=4
i =2 , j =2 , n=5
i =2 , j =3 , n=6
i =2 , j =4 , n=7
i =3 , j =3 , n=8
i =3 , j =4 , n=9
i =4 , j =4 , n=10
$
47
El c
odigo traducido al ensamblador, que retornara n al Sistema Operativo,
sera el siguiente:
C
odigo fuente 3.11: t5 for for.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#===[C e q u i v a l e n t e]============
# n=0 ;
# f o r ( i =1 ; i <5; i ++)
#
f o r ( j=i ; j <5; j ++)
#
n++; r e t n ;
.text
# code segment
. g l o b a l main
main :
# main ( ) {
movl $0 , %eax
movl $1 , %e s i
fori :
cmpl $5 , %e s i
jge e n d f o r i # %es i >=5
movl %es i , %edi
forj :
cmpl $5 , %edi
jge e n d f o r j # %edi>=5
i n c l %eax
i n c l %edi
jmp f o r j
endforj :
i n c l %e s i
jmp f o r i
endfori :
# LINUX EXIT
movl %eax , %ebx
movl $1 , %eax
i nt $0x80
$ g c c g o t 5 f o r f o r t 5 f o r f o r . s
$ . / t 5 f o r f o r ; echo $ ?
10
$
EL CODIGO
48
3.11.
# D e f i n i c i o n de c o n s t a n t e s
N = 20
# decimal
X = 0x4A
# hexadecimal
B = 0 b010011 # b i n a r i o
C = J
# caracter
.data
# declaracion variables i nit iali zad as
i i : . l o n g 1 # i nt i i = 1 ;
s s : .wo r d 2 # short s s = 2 ;
c c : . b y t e 3 # cha r c c = 3 ;
s1 : . a s c i i Hola \n # Chars
s2 : . a s c i z Hola \n # Chars+\0
. b s s # d e c l a r a c i o n v a r i a b l e s no i n i t i a l i z a d a s
. g l o b a l nom var # v a r i a b l e g l o b a l
#( v i s i b l e desde f i c h e r o s e x t e r n o s )
# .comm nom
.comm i , 4 ,
.comm s , 2 ,
.comm c , 1 ,
var ,
4 #
2 #
1 #
tamano , a l i n e a c i o n
i nt i ;
short s ;
cha r c ;
. t e x t # S e c c i o n de c o d i g o
. g l o b a l main # main v i s i b l e por e l SO
main :
...
# F i n a l i z a r e l programa en Linux
movl $0 , %ebx
# Ret %bl a l SO
movl $1 , %eax
# $1 = f u n c i o n s y s e x i t d e l SO
i nt $0x80
# Ejecuta l a i n t e r r u p c i o n
Captulo 4
Vectores
4.1.
Declaraci
on y uso de vectores
Las declaraciones de vectores inicializados a un determinado valor se hacen en el bloque .data de la siguiente manera: nombre variable: .tipo valor.
Ejemplo de declaraci
on de vectores con su equivalencia en C:
1 .data
2 vl : .long 1 ,2 ,3
# i nt
3 vw : .wo r d 1 , 2 , 3
# short
4 vb : . b y t e 1 , 2 , 3
# cha r
5 s1 : . a s c i i abd
# cha r
6 s2 : . a s c i z abd
# cha r
7 #. a s c i z guarda una p o s i c i o n
vl [ 3 ]
vw [ 3 ]
vb [ 3 ]
s1 [ 3 ]
s2 [ 4 ]
extra
= {1 ,2 ,3} ;
= {1 ,2 ,3} ;
= {1 ,2 ,3} ;
= { a , b , c } ;
= { a , b , c , \0 } ;
con e l cha r \0
.bss
.comm uvl , 3 4 , 4
.comm uvw , 3 2 , 2
.comm uvb , 3 , 1
# i nt u v l [ 3 ]
# short uvw [ 3 ]
# cha r uvb [ 3 ]
CAPITULO 4. VECTORES
50
3
4
5
6
7
8
9
10
11
12
# Y s i %ebx apunta a v
movl $0 , ( %ebx, %ecx , 4 )
# Con word :
movl $0 , v(, %ecx , 2 ) # v [ i ] = 0 ;
# Y s i %ebx apunta a v
movl $0 , ( %ebx, %ecx , 2 )
# Con cha r :
movb $ a , c( %ecx ) # c [ i ]= a ;
# Y s i %ebx apunta a c
movl $ a , ( %ebx, %ecx )
Veamos ahora un peque
no ejemplo:
Codigo fuente 4.1: t6 vectors.s
1 .data
2 vw : .wo r d 4 , 5 , 6 # short vw[ 3 ] = { 4 , 5 , 6 } ;
3 .text
4 . g l o b a l main
5 main :
6
movw $1 , %cx
# i =1 ;
7
movswl %cx , %ebx
# Trampa !
8
# Rb & Ri t i e n e n que s e r de 32 b i t s !
9
movw %cx , vw(, %ebx , 2 )
# vw [ i ]= i ;
10
# Ahora vw={4 ,1 ,6}
11
movl vw(, %ebx , 2 ) , %ebx
12
13
movl $1, %eax
14
i nt $0x80
$ g c c g o t 6 v e c t o r s t 6 v e c t o r s . s
$ ./ t6 vectors
$ echo $ ?
1
TRAMPAS
En direccionamiento de memoria Rb y Ri tienen que ser registros
de 32 bits! Si en lugar de extender %cx a %ebx y luego referirnos
a vw(, %ebx,2) nos refirieramos a vw(, %cx,2) nos dara un error
51
de compilaci
on diciendonos Error: vw(, %cx,2) no es una expresion
base/ndice valida:
$ g c c g o t 6 v e c t o r s t 6 v e c t o r s . s
t6 vectors . s : Assembleriviestit :
t 6 v e c t o r s . s : 9 : Vir he : vw(, %cx , 2 ) e i o l e
vo ima ssa o l e v a ba se / i n d e x l a u s e k e
$
4.2.
CAPITULO 4. VECTORES
52
24
25
26
27
# LINUX EXIT
movl n , %ebx
movl $1 , %eax
i nt $0x80
# return n
$ g c c g o t 6 r e c o r r i d o a t 6 r e c o r r i d o a . s
$ ./ t6 recorrido a
$ echo $ ?
7
$
EL CODIGO
Lnea 7: Usaremos la variable n para almacenar el n
umero de as
encontrado.
Lnea 12: Inicializamos %ecx a cero. %ecx se suele usar como registro
para recorrer vectores (al igual que la variable i en C). %esi tambien
es una buena opcion.
Lneas 13-15: Empieza el bucle y saldremos del bucle cuando la posici
on %ecx del vector sea igual a ..
Lneas 16-19: Si la posicion %ecx del vector es igual a a incrementamos el contador de as n.
Lneas 20-21: Incrementamos %ecx y pasamos a la siguiente iteraci
on
del bucle.
4.3.
Y aqu tenemos un ejemplo donde recorremos un vector de longs (finalizada en cero) y retornamos al sistema cuantas as hay en la cadena de caracteres. Es muy similar al anterior. Las diferencias principales es que usamos el
registro %eax para acumular la suma de los elementos recorridos ( %eax es un
buen registro para utilizar como acumulador) y que ahora el vector es de longs.
Si v es un vector de longs: El equivalente en C de v[i] es v(, %ecx,4).
Ya que v(, %ecx,4) devuelve v+ %ecx*4(donde v es la direcci
on de memoria
donde empieza el vector).
4.4. BUSQUEDA
EN VECTOR DE BYTE
53
C
odigo fuente 4.3: t6 recorrido i.s
1 # Retorna l a suma de l o s e l e m e n t o s de un v e c t o r
acabado en 0 .
2 # . / t 6 r e c o r r i d o i ; echo $?
3 # 45
4
5 .data
6 v : . i n t 1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,0
7
8 .text
9 . g l o b a l main
10 main :
11
movl $0 , %eax
12
movl $0 , %ecx
# for ( i = 0 ;
13 f o r :
14
cmpl $0 , v(, %ecx , 4 )
# f o r (? ; v [ i ]!=0;?) {
15
je endfor
16
a ddl v(, %ecx , 4 ) , %eax
# a=a+v [ i ] ;
17
i n c l %ecx
# f o r ( ? ; ? ; i ++)
18
jmp f o r
19 e n d f o r :
20
21
# LINUX EXIT
22
movl %eax , %ebx # r e t u r n a
23
movl $1 , %eax
24
i nt $0x80
$ g c c g o t 6 r e c o r r i d o i t 6 r e c o r r i d o i . s
$ ./ t6 recorrido i
$ echo $ ?
45
$
4.4.
B
usqueda en vector de byte
CAPITULO 4. VECTORES
54
EL CODIGO
Lneas 14-21: Recorremos el vector hasta encontrar o una a o el punto
4.5. BUSQUEDA
EN VECTOR DE LONGS
55
4.5.
B
usqueda en vector de longs
Y, finalmente, un ejemplo de b
usqueda en un vector de longs que devuelve
la posicion del primer n
umero negativo en un vector v acabado en cero; o -1
si no hubiese ning
un n
umero negativo en el vector.
C
odigo fuente 4.5: t6 busqueda i.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#
#
#
#
Retorna l a p o s i c i o n d e l pr imer n e g a t i v o en v .
. / t 6 b u s q u e d a i ; echo $?
5
Si no hay ningun neg r e t o r n a 255 ( 1)
.data
v : . i n t 1 ,2 ,3 ,4 ,5 , 6 ,7 ,8 ,9 ,0
.text
. g l o b a l main
main :
movl $0 , %ecx
# for
for :
cmpl $0 , v(, %ecx , 4 )
je endfor
cmpl $0 , v(, %ecx , 4 )
j l endfor
i n c l %ecx
# for
jmp f o r
endfor :
cmpl $0 , v(, %ecx , 4 )
jge e l s e
movl %ecx , %eax # a =
jmp endif
else :
movl $ 1 , %eax # a =
endif :
( i = 0 ; // e c x = i
# f o r ( ? ; v [ i ]!= 0
# && v [ i ]>=0 ; ?) {
( ? ; ? ; i ++)
# i f ( v [ i ] <0)
i;
1 ; ( 2 5 5 )
# LINUX EXIT
movl %eax , %ebx # r e t u r n a
CAPITULO 4. VECTORES
56
31
32
movl $1 , %eax
i nt $0x80
$ g c c g o t 6 b u s q u e d a i t 6 b u s q u e d a i . s
$ ./ t6 busqueda i
$ echo $ ?
5
$
4.6.
Vector de words
57
$ g c c g o t 6 p 0 3 t 6 p 0 3 . s
$ . / t6 p03
$ echo $ ?
9
$
EL CODIGO
Lnea 1: La constante N nos indica la longitud del vector v.
Lneas 3-6: El vector v se declara como vector de words y las variables
min y max como words.
Lneas 7-13: Se asigna la primera posicion del vector a min y a max.
Lneas 14-35: Se recorre el resto del vector. Por cada posicion del
vector, si su valor es menor que min (el valor mnimo de la parte
del vector que ya hemos recorrido) copiamos esa posicion del vector a
min; y si su valor es mayor que max copiamos esa posicion a max.
CAPITULO 4. VECTORES
58
TRAMPAS
Lneas 11-13: No podemos escribir movw v(, %ecx,2), min (que es lo
que nos pedira el cuerpo) porque una instruccion solo puede tener
un operando en memoria (y las variables est
an en memoria). Por eso
tenemos que usar un registro auxiliar ( %dx es un buen registro para
almacenar un dato). Si en la lnea 11 tuvieramos movw v(, %ecx,2),
min nos dara un error de compilaci
on diciendonos Error: demasiadas referencias a memoria:
g c c g o t 6 p 0 3 t 6 p 0 3 . s
t6 p03 . s : A s s e m b l e r i v i e s t i t :
t 6 p 0 3 . s : 1 1 : Vir he : l i i a n monta m u i s t i v i i t t a u s t a
m a l l i n t e e s e e n mov
$ LANG=C g c c g o t 6 p 0 3 t 6 p 0 3 . s
t 6 p 0 3 . s : Assembler messa g es :
t 6 p 0 3 . s : 1 1 : E r r o r : t o o many memory r e f e r e n c e s
for mov
$
Lneas 21 y 27: Lo mismo sucede con las comparaciones (cmpw).
COMPLEJOS
4.7. EJEMPLOS MAS
59
EL BOXEO
El ensamblador es como el boxeo porque todo va al reves.
Excepto en el funcionamiento de los vectores donde, como habeis podido ver, todo funciona como esperaramos.
4.7.
Ejemplos m
as complejos
4.7.1.
Ejemplo 1
60
CAPITULO 4. VECTORES
COMPLEJOS
4.7. EJEMPLOS MAS
61
45 f o r p r i n t f :
46
cmpb $0 , ov( %edi )
47
j e e n d f o r p r i n t f # oc( %edi ) == 0
48
49
p u s h l ov( %edi )
50
pushl $s
51
call printf
52
a ddl $8 , %esp
53
54
i n c l %edi
55
jmp f o r p r i n t f
56 e n d f o r p r i n t f :
57
58
pushl $sr
59
call printf
60
a ddl $4 , %esp
61
62 # END DEBUG
63
64
movl $0 , %ebx
65
movl $1 , %eax
66
i nt $0x80
$ g c c g o t 6 f o o b t 6 f o o b . s
$ ./ t6 foob
tres
$
4.7.2.
Ejemplo 2
CAPITULO 4. VECTORES
62
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
.bss
.comm c e r o , 4 , 4
.comm p o s i , 4 , 4
.comm nega , 4 , 4
.text
. g l o b a l main
main :
movl $0 , c e r o
movl $0 , p o s i
movl $0 , nega
movl $0 , %ecx
etiq for :
cmpl n , %ecx
jge e t i q e n d f o r # %ecx >= n
cmpl $0 , v(, %ecx , 4 )
# j e e t i q c e r o # == 0
jg e t i q p o s i # > 0
jl etiq nega # < 0
etiq cero :
i n c l cero
jmp e t i q f o r i n c
etiq posi :
incl posi
jmp e t i q f o r i n c
etiq nega :
i n c l nega
# jmp e t i q f o r i n c
etiq forinc :
i n c l %ecx
jmp e t i q f o r
etiq endfor :
# BEGIN DEBUG
p u s h l %ecx
p u s h l nega
pushl posi
pushl cero
pushl $s
call printf
a ddl $16 , %esp
po pl %ecx
# END DEBUG
movl $0 , %ebx
movl $1 , %eax
COMPLEJOS
4.7. EJEMPLOS MAS
55
63
i nt $0x80
$ g c c g o t 6 f o o l t 6 f o o l . s
$ ./ t6 fool
Cer o s : 1 . P o s i t i v o s : 4 . N e g a t i v o s : 3 .
$
4.7.3.
Ejemplo 3
CAPITULO 4. VECTORES
64
31
jmp f o r
32 e n d f o r :
33
34 # BEGIN DEBUG
35
movl $0 , %ecx
36 f o r p r i n t f :
37
cmpb $0 , ov( %ecx )
38
j e e n d f o r p r i n t f # oc( %ecx ) == 0
39
40
p u s h l %ecx
41
p u s h l ov( %ecx )
42
pushl $s
43
call printf
44
a ddl $8 , %esp
45
po pl %ecx
46
47
i n c l %ecx
48
jmp f o r p r i n t f
49 e n d f o r p r i n t f :
50
51
pushl $sr
52
call printf
53
a ddl $4 , %esp
54
55 # END DEBUG
56
57
movl $0 , %ebx
58
movl $1 , %eax
59
i nt $0x80
$ g c c g o t 6 b a r b t 6 b a r b . s
$ ./ t6 barb
Hay mucho espacio en blanco por aqui .
$
4.7.4.
Ejemplo 4
Dada una variable entera n con valor entre 0 y 20 hacer un programa que
imprima por pantalla los primeros n elementos de la sucesion de Fibonacci. En
el ejemplo n vale 20 pero funciona con cualquier n entre 0 y 20.
La sucesion de Fibonacci es tal que f[0]=0, f[1]=1, y para i =1, f[i]=f[i1]+f[i-2].
Codigo fuente 4.10: t6 barl.s
1 # F i b o n a c c i en un v e c t o r de n e l e m e n t o s .
COMPLEJOS
4.7. EJEMPLOS MAS
2 # n d e c l a r a d a como i n i c i a l i z a d a . El v e c t o r como no
inicializado.
3 SIZE = 20
4 .data
5 n : . l o n g 20
6 # BEGIN DEBUG
7 s : . a s c i z %d\n
8 # END DEBUG
9 .bss
10 .comm v , SIZE 4 , 4
11 . t e x t
12 . g l o b a l main
13 main :
14
cmpl $1 , n
15
j l end # n < 1
16
movl $0 , %ecx
17
movl $0 , v(, %ecx , 4 )
18
19
cmpl $2 , n
20
j l end # n < 2
21
movl $1 , %ecx
22
movl $1 , v(, %ecx , 4 )
23
24
movl $0 , %e s i
25
movl $1 , %edi
26
movl $2 , %ecx
27 f o r :
28
cmpl n , %ecx
29
jg e n d f o r # %ecx > n
30
31
movl v(, % es i , 4 ) , %eax
32
a ddl v(, %edi , 4 ) , %eax
33
movl %eax , v(, %ecx , 4 )
34
i n c l %e s i
35
i n c l %edi
36
i n c l %ecx
37
jmp f o r
38 e n d f o r :
39
40 end :
41 # BEGIN DEBUG
42
movl $0 , %ecx
43 f o r p r i n t f :
44
cmpl n , %ecx
45
jge e n d f o r p r i n t f # %ecx >= n
46
65
66
47
p u s h l %ecx
48
p u s h l v(, %ecx , 4 )
49
pushl $s
50
call printf
51
a ddl $8 , %esp
52
po pl %ecx
53
54
i n c l %ecx
55
jmp f o r p r i n t f
56 e n d f o r p r i n t f :
57 # END DEBUG
58
59
movl $0 , %ebx
60
movl $1 , %eax
61
i nt $0x80
$ g c c g o t 6 b a r l t 6 b a r l . s
$ ./ t6 barl
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
$
CAPITULO 4. VECTORES
Captulo 5
Subrutinas
5.1.
La pila (stack)
La pila (o stack en ingles) es una pila de bytes LIFO: Last In, First Out.
La pila tiene asociado un puntero a su cima ( %esp) que se inicializa por el Sistema Operativo cuando carga el programa en memoria.
La pila crece en sentido decreciente de las direcciones de memoria (anti-intuitivo).
Por lo tanto, la cima tiene una posicion de memoria inferior a la base.
En las arquitecturas de 32 bits apilaremos y desapilaremos longs.
Podemos pensar en la pila como en una pila de libros que tenemos encima
de la mesa. Si apilo un libro siempre lo apilare en su cima. Y, si desapilo un
libro, siempre sera el libro que estaba encima de todo (el que estaba en la cima).
Para apilar el long op1 haremos:
1
p u s h l op1
Equivale a:
1
2
s u b l $4 , %esp
movl op1 , ( %esp )
Para desapilar el long que est
a en la cima de la pila y guardar ese valor en op1
haremos:
po pl op1
Equivale a:
1
2
CAPITULO 5. SUBRUTINAS
68
Aqu vemos una pila (@- y @- sirven para recordarnos de que las posiciones de la pila de mas arriba tienen direcciones de memoria menores a las
posiciones de mas abajo). Tiene un u
nico long (que vale 1) y %esp (que siempre apunta a la cima de la pila) apunta a ese elemento. Si la direcci
on de memoria
donde est
a su u
nico elemento fuera la 1000, la posicion inmediatamente superior
sera la 996, y la siguiente 992, ya que cada posicion de la pila es un long (4
bytes).
@-
@+
0x00000001
%esp
p u s h l %eax
La pila pasar
a a tener una copia de %eax y %esp se habr
a decrementado en
4 porque un long ocupa 4 bytes:
@-
@+
0x12345678
0x00000001
%esp
4( %esp)
p u s h l %ebx
La pila pasar
a a tener una copia de %ebx encima de la copia de %eax y, de
nuevo, %esp se actualizar
a decrement
andose en 4:
@-
@+
0xFFFFFFFF
0x12345678
0x00000001
-4( %esp)
%esp
4( %esp)
8( %esp)
69
po pl %ecx
Con esto, se desapilar
a el elemento que tiene en la cima (0xFFFFFFFF) y
se asignar
a a %ecx y, %esp se actualizar
a increment
andose en 4:
@-
@+
0x12345678
0x00000001
-8( %esp)
-4( %esp)
%esp
4( %esp)
a ddl $4 , %esp
Con esto, se desapilar
a el elemento que tiene en la cima:
@-
@+
0x00000001
-12( %esp)
-8( %esp)
-4( %esp)
%esp4
TEORIA
RESUMEN DE LA PILA
Para apilar el long op1:
1
p u s h l op1
Para desapilar un long y guardarlo en op1:
po pl op1
Desapilar el long de la cima sin guardarlo:
CAPITULO 5. SUBRUTINAS
70
a ddl $4 , %esp
%esp Siempre apunta al elemento que est
a en la cima de la pila.
EL BOXEO
El ensamblador es como el boxeo porque todo va al reves:
La direcci
on de memoria de la cima de la pila es mas peque
na que la
direcci
on de la base. Y, para reducir el tama
no de la pila le sumas al
stack pointer el n
umero de bytes que quieres desapilar y descartar.
5.2.
Para invocar una subrutina que ya existe, (como por ejemplo printf) si en
C hacamos
1
p r i n t f ( H e l l o World ! \ n ) ;
En ensamblador haremos:
push de los par
ametros empezando por el de la derecha (y si un par
ametro
es un vector, pasaremos el puntero al vector).
call subrutina
Si la subrutina retorna alg
un valor lo dejara en %eax.
Eliminar los par
ametros de la pila (equivalente a hacer un pop pero el
par
ametro no se guarda en ning
un registro).
Codigo fuente 5.1: t7 ex printf.s
1
2
3
4
5
6
7
.data
s : . a s c i z H e l l o World ! \ n
.text
. g l o b a l main
main :
# Pasar param ( punter o a s )
pushl $s
71
# Llamar s u b r u t i n a
call printf
# E l i m i n a r param de l a p i l a
a ddl $4 , %esp
movl $1 , %eax
movl $0 , %ebx
i nt $0x80
$ ./ t7 ex printf
H e l l o World !
$
TRAMPAS
El string de printf() tiene que acabar en \n o puede que no aparezca
por pantalla (a diferencia de en C).
EL BOXEO
El ensamblador es como el boxeo porque todo va al reves:
El u
ltimo par
ametro de una llamada a subrutina se apila primero y
el primer par
ametro se apila el u
ltimo.
5.3.
call etiqueta
Esto equivale a:
1
2
p u s h l %e i p
movl e t i q u e t a , %e i p
CAPITULO 5. SUBRUTINAS
72
ret
Que equivale a:
po pl %e i p
Con esto actualizamos %eip con la llamada direcci
on de retorno.
El registro %eip no se puede modificar directamente. De forma que las llamada a
una subrutina son saltos al c
odigo de esa subrutina despues de la cual podemos
volver a la linea siguiente de la que ha llamado a la subrutina.
Por cierto, los par
ametros (que se pasan de derecha a izquierda) se guardan en
direcciones de memoria alineadas a 4 bytes. Si un par
ametro es un char o un
word, primero se convierte en long y luego se apila con pushl. Ejemplo de una
llamada a subrutina(a):
1
2
3
4
movb $ a , %a l
movzbl %al , %eax
p u s h l %eax
call subrutina
El valor de retorno de una funci
on se deja en %eax, %ax, o %al dependiendo
de si la funci
on devuelve un long un word o un byte.
5.4.
73
5.5.
printf y scanf
.data
i : . i n t 2 1 , 1 5 , 3 4 , 1 1 , 6 , 5 0 , 3 2 , 8 0 , 1 0 , 0 # Acabado
en 0
s : . a s c i z El numero e s : %d\n # . a s c i z es como . a s c i i
per o acabado en \0
.text
. g l o b l main
main :
movl $0 , %ecx
loop :
p u s h l %ecx
# 1 S a l v a r eax , ecx , edx
p u s h l i (, %ecx , 4 )
# 2 Pasar params
pushl $s
# 2 Pasar params
call printf
# 3 Llamar s u b r u t i n a
# 12 R e s u l t a d o en eax
a ddl $8 , %esp
# 13 E l i m i n a r params
po pl %ecx
# 14 R e s t a u r a r eax , ecx , edx
i n c l %ecx
cmpl $0 , i (, %ecx , 4 )
jne loop
movl $1 , %eax
movl $0 , %ebx
i nt $0x80
EL CODIGO
En este ejemplo, como de los registros rocanroleros solo nos
importa %ecx ( %eax y %edx no los usamos y nos da igual si la
CAPITULO 5. SUBRUTINAS
74
$ g c c g o t 7 p r i n t f t 7 p r i n t f . s
$ ./ t7 printf
El numero e s : 21
El numero e s : 15
El numero e s : 34
El numero e s : 11
El numero e s : 6
El numero e s : 50
El numero e s : 32
El numero e s : 80
El numero e s : 10
$
Veamos ahora otro ejemplo un poco mas completo donde imprimimos el
indice del vector junto con el valor contenido en esa posicion:
Codigo fuente 5.3: t7 printf2.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.data
i : . i n t 21 , 15 , 34 , 11 , 6 ,
en 0
s : . a s c i z i [ %d ] = %d\n #
acabado en \0
.text
. g l o b l main
main :
movl $0 , %ecx
loop :
p u s h l %ecx
p u s h l i (, %ecx , 4 )
p u s h l %ecx
pushl $s
call printf
a ddl $12 , %esp
po pl %ecx
5 0 , 3 2 , 8 0 , 1 0 , 0 # Acabado
. a s c i z es como . a s c i i per o
#
#
#
#
#
#
#
#
i n c l %ecx
cmpl $0 , i (, %ecx , 4 )
jne loop
movl $1 , %eax
movl $0 , %ebx
i nt $0x80
$ g c c g o t 7 p r i n t f 2 t 7 p r i n t f 2 . s
$ ./ t7 printf2
i [ 0 ] = 21
i [ 1 ] = 15
i [ 2 ] = 34
i [ 3 ] = 11
i [4] = 6
i [ 5 ] = 50
i [ 6 ] = 32
i [ 7 ] = 80
i [ 8 ] = 10
$
Igual que invocamos printf podemos invocar scanf:
C
odigo fuente 5.4: t7 scanf.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.data
s : . a s c i z I n t r o d u c e un num :
i s : . a s c i z %d
o s : . a s c i z El num e s : %d . S c a n f r e t o r n a %d . \n
.bss
.comm n , 4 , 4
.text
. g l o b a l main
main :
#p r i n t f ( s ) ;
pushl $s
call printf
a ddl $4 , %esp
#s c a n f ( i s , &n ) ;
p u s h l $n
pushl $ i s
call scanf
a ddl $8 , %esp
#p r i n t f ( os , n , eax )
p u s h l %eax
75
CAPITULO 5. SUBRUTINAS
76
23
24
25
26
27
28
29
30
pushl n
pushl $os
call printf
a ddl $12 , %esp
movl $0 , %ebx
movl $1 , %eax
i nt $0x80
$ g c c g o t 7 s c a n f t 7 s c a n f . s
$ ./ t7 scanf
I n t r o d u c e un num : 7
El num e s : 7 . S c a n f r e t o r n a 1 .
$
EL CODIGO
Fijaos que en la lnea 16 hacemos un pushl de $n en lugar de n
porque el par
ametro es un puntero a n (&n en C), es decir su posicion
de memoria.
De la misma manera, en la lna 17 hacemos un pushl de $is en lugar
de is porque los strings son vectores de car
acteres, y los vectores
se pasan como par
ametro pasando la direcci
on de memoria donde
empiezan.
Veamos ahora otro ejemplo un poco mas completo donde leemos dos enteros:
Codigo fuente 5.5: t7 scanf 2.s
1
2
3
4
5
6
7
8
9
10
.data
sp : . a s c i z I n t r o 2 nums :
s s : . a s c i z %d %d
sp2 : . a s c i z Los nums son : %d y %d . S c a n f r e t o r n a %d . \
n
.bss
.comm n1 , 4 , 4
.comm n2 , 4 , 4
.comm ret , 4 , 4
.text
. g l o b l main
5.6. CODIGO
DE UNA SUBRUTINA
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
77
main :
p u s h l $sp
call printf
a ddl $4 , %esp
p u s h l $n2
p u s h l $n1
pushl $ss
call scanf
a ddl $12 , %esp
movl %eax , ret
p u s h l ret
p u s h l n2
p u s h l n1
p u s h l $sp2
call printf
a ddl $16 , %esp
movl $0 , %ebx
movl $1 , %eax
i nt $0x80
$ g c c g o t 7 s c a n f 2 t 7 s c a n f 2 . s
$ ./ t7 scanf 2
I n t r o 2 nums : 3 7
Los nums son : 3 y 7 . S c a n f r e t o r n a 2 .
$
5.6.
C
odigo de una subrutina
subrutina :
p u s h l %ebp
movl %esp , %ebp
CAPITULO 5. SUBRUTINAS
78
po pl %ebp # d e s h a c e r e l e n l a c e
ret #po pl %e i p
5.6. CODIGO
DE UNA SUBRUTINA
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
79
p u s h l %edx
c a l l minuscula
a ddl $4 , %esp
po pl %ecx
movb %al , o s( %ecx )
i n c l %ecx
jmp f o r
endfor :
movb $0 , o s( %ecx )
pushl $os
call printf
a ddl $4 , %esp
movl $0 , %ebx
movl $1 , %eax
i nt $0x80
minuscula :
p u s h l %ebp
movl %esp , %ebp
movb 8( %ebp ) , %a l
# %a l < $ A
cmpb $ A , %a l
j l minendif
# %a l > $ Z
cmpb $ Z , %a l
jg m i n e n d i f
addb $ a A , %a l
minendif :
po pl %ebp
ret
$ g c c g o t 7 m i n u s c u l a s p r i n t 2 t 7 m i n u s c u l a s p r i n t 2 . s
$ ./ t7 minusculas print 2
Demasiadas Mayusculas PARA TAN pOcAs BalAs .
dema sia da s ma yuscula s pa r a tan po ca s b a l a s .
$
CAPITULO 5. SUBRUTINAS
80
EL CODIGO
linea 18: Empieza la llamada a la subrutina minuscula. Para ello
apilamos los registros %eax, %ecx y %edx si es que no queremos perder
el valor que contienen. En este caso solo %ecx.
linea 19: El par
ametro con el que invocamos la subrutina es de tipo
byte. Por ello lo pasamos a long con movsbl.
linea 20: Apilamos el par
ametro ya convertido a long.
linea 21: Llamamos a la subrutina
linea 22: Al volver, desapilamos el par
ametro.
linea 23: Recuperamos el valor de %ecx que habamos guardado en la
pila.
linea 24: El resultado de la funci
on siempre se guarda en %eax. Esta
vez como minuscula retorna un byte, el resultado (el car
acter pasado
a min
usculas ) est
a en %al.
linea 38: La subrutina minuscula. Las subrutinas siempre empiezan
con las siguientes dos lineas y siempre terminan con las dos u
ltimas
(lineas 52 y 53).
linea 39: Apilo (guardo una copia de) %ebp
linea 39: Muevo el valor de %esp a %ebp.
lineas 42-51: El cuerpo de la subrutina.
linea 52: Recupero el valor de %ebp (la base de la pila de la subrutina
que me ha llamado).
linea 53: Retorno.
TRAMPAS
linea 3: La cadena de caracteres que se pasan a printf() tiene que
81
TRUCOS
La funci
on minuscula retorna un byte que dejamos en %al. Es por eso
que solo modificamos %al sin preocuparnos de lo que haya en el resto
de %eax. No obstante, puede ser una buena costumbre asegurarse de
que el resto de %eax este a cero. Esto es u
til de cara a debugar (donde
al ver los registros veremos %eax) o para evitarse errores. En este
c
odigo una opci
on sera cambiar la linea 48 el movb por un movzbl:
42
5.7.
De la misma manera que antes de llamar a una subrutina hay que guardar
en la pila los valores de los registros rocanroleros ( %eax, %ecx y %edx) que no
queremos que sean modificados porque los estamos usando, dentro de la funci
on,
CAPITULO 5. SUBRUTINAS
82
justo despues de establecer el enlace dinamico hay que guardar en la pila los
registros que denominaremos registros base e ndices ( %ebx, %esi, %edi) que
modifiquemos dentro de la funci
on. A falta de un mnemotecnico mejor para los
registros base e ndices podemos pensar en c
omo pronunciaras BDC en ingles
(bi, si, di).
El c
odigo completo de la subrutina sera el siguiente:
La etiqueta con el nombre de la funci
on y el Establecimiento del enlace
din
amico (que consiste en apilar %ebp y mover el valor de %esp a %ebp).
1
2
3
subrutina :
p u s h l %ebp
movl %esp , %ebp
De manera que una vez establecido el enlace dinamico el registro %ebp
contiene una posicion de memoria de referencia de esta subrutina, donde:
( %ebp) Apunta al %ebp de la subrutina que nos ha invocado
4( %ebp) Apunta a la direcci
on a la que debemos retornar al terminar
la subrutina
8( %ebp) Apunta al primer par
ametro (de existir)
12( %ebp) Apunta al segundo par
ametro (de existir)
Etcetera.
Salvar registros %ebx, %esi, %edi: Guardamos en la pila una copia de
los registros ndice y bases que se modifican en esta subrutina.
A continuacion viene el cuerpo de la subrutina.
Y, al terminar la ejecuci
on de la subrutina deberemos mover el resultado
que devuelve la subrutina a %eax
Restaurar registros %edi, %esi, %ebx: Restauramos de la pila la copia
de los registros ndice y bases que se han modificado en esta subrutina.
Deshacer el enlace y retornar (recuperar la direcci
on de retorno y moverla
a %eip).
1
2
5.8.
po pl %ebp # d e s h a c e r e l e n l a c e
ret #po pl %e i p
Finalmente, para terminar de complicarlo todo. Vamos a ver como implementaramos una subrutina que tuviera variables locales.
Pues bien justo despues de establecer el enlace dinamico, y antes de salvar los
83
registros base e ndice, reservaremos espacio en la pila para las variables locales.
Esto lo haremos restando a %esp el n
umero de bytes (alineado a 4 bytes) que
necesitamos. Si las variables locales son tres longs (12 bytes en total), haremos:
1
s u b l $12 , %esp
# 12 b i t s pa r a v a r i a b l e s l o c a l e s
Una vez hecho esto para acceder a las 3 variables locales podemos usar 4( %ebp), -8( %ebp) y -12( %ebp). Si por ejemplo, quisieramos inicializarlas
a cero haramos:
1
2
3
movl $0 , 4( %ebp )
movl $0 , 8( %ebp )
movl $0 , 12( %ebp)
# I n i c i a l i z a r VarLoc1 a 0
# I n i c i a l i z a r VarLoc2 a 0
# I n i c i a l i z a r VarLoc3 a 0
# 12 b i t s pa r a v a r i a b l e s l o c a l e s
CAPITULO 5. SUBRUTINAS
84
9
10
11
12
13
14
15
i nt m=0;
while ( b != 0 ) {
m=m+a ;
b;
}
return m;
}
$ g c c g o t 7 m u l t i p l i c a t 7 m u l t i p l i c a . c
tarom@sisu : / Dropbox/TAROM/FO/0 Books/ s r c $ . / t 7 m u l t i p l i c a
16
$
Y ahora la versi
on en ensamblador:
Codigo fuente 5.8: t7 multiplica.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
.text
. g l o b a l main
main :
p u s h l $3
p u s h l $2
call multiplica
a ddl $8 , %esp
movl %eax , %ebx
p u s h l $2
p u s h l $5
call multiplica
a ddl $8 , %esp
a ddl %eax , %ebx
movl $1 , %eax
i nt $0x80
multiplica :
p u s h l %ebp
movl %esp , %ebp
s u b l $4 , %esp
p u s h l %ebx
movl $0 , 4( %ebp)
movl 8( %ebp) , %eax
movl 12( %ebp ) , %ebx
multi loop :
cmp $0 , %ebx
je multi endloop
85
EL CODIGO
hasta la linea 22: El main no tiene ning
un secreto. Invocamos dos
veces a multiplica con los dos n
umeros a multiplicar y el resultado de
las dos multiplicaciones la guardamos en %ebx y finaliza la ejecuci
on.
Y en la subrutina multiplica establecemos el enlace dinamico (lineas
20 y 21).
linea 22: Reservamos espacio para la variable local.
linea 23: Estado de la pila despues de reservar espacio para la variable
local en la primera invocacion de la subrutina multiplica y salvar
los registros base e ndices:
@%ebx
%esp l23 pushl %ebx
( %ebp-4)
VarLoc
l22 subl $4, %esp
( %ebp)
%ebp main %ebp l20 push %ebp
( %ebp+4)
@ret
l06 call multiplica
( %ebp+8)
$2
l05 pushl $2
( %ebp+12)
$3
l04 pushl $3
Antes
de
invocar
a
multiplica
salvaramos
los
registros %eax, %ecx, %ebx que usamos. En este caso ninguno.
Luego apilaramos los par
ametros con los que invocamos multiplica empezando por el u
ltimo (l04 y l05).
Hacemos el call (l06) que hace que nos apile la direcci
on de
retorno (direccion a la que tenemos que saltar al hacer un ret)
y salte a la etiqueta de la subrutina multiplica.
Establecemos el enlace dinamico salvando el base pointer del
main (l20) y haciendo que el nuevo %ebp (el %ebp de multiplica) apunte a la cima de la pila actual (l21).
CAPITULO 5. SUBRUTINAS
86
Vamos a ver otro ejemplo donde tiene mas sentido el uso de variables locales.
En el siguiente programa (t7 signos.s) tenemos una subrutina llamada signos
a la cual se le pasan dos par
ametros. El primero un entero y el segundo un
vector de enteros (vamos a llamarlos n y v). En C esto equivaldra a void
signos(int n, int *v). Pues bien la subrutina recorre las primeras n posiciones
del vector v e imprime por pantalla cuantos de estos enteros son cero, cuantos
son enteros positivos y cuantos son enteros negativos. Para ello utiliza tres variables long que usa como contadores de ceros, positivos y negativos. Para que se
vea f
acilmente que el programa funciona adecuadamente, la subrutina signos
ir
a imprimiendo los enteros del vector a medida que lo vaya recorriendo.
Aqu vemos la compilaci
on y ejecuci
on del programa donde la funci
on main
invoca a la subrutina signos con tres vectores de enteros diferentes:
$ g c c g o t 7 m u l t i p l i c a t 7 m u l t i p l i c a . s
$ ./ t7 multiplica
$ echo $ ?
16
87
$
Y aqu tenemos el c
odigo fuente. Sigue los comentarios e intenta entender
que va haciendo linea a linea:
C
odigo fuente 5.9: t7 signos.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
.data
s : . a s c i z \ nCeros: %d . P o s i t i v o s: %d . N e g a t i v o s: %d . \n
sd : . a s c i z %d ,
n1 : . l o n g 8
v1 : . l o n g 7 ,9 ,2 ,3 , 4 , 5 , 6 ,0
n2 : . l o n g 6
v2 : . l o n g 1 ,2 ,3 , 4 , 5 , 6
n3 : . l o n g 1
v3 : . l o n g 1
.text
. g l o b a l main
main :
p u s h l $v1
p u s h l n1
call signos
# s i g n o s ( n1 , v1 )
a ddl $8 , %esp
p u s h l $v2
p u s h l n2
call signos
a ddl $8 , %esp
p u s h l $v3
p u s h l n3
call signos
a ddl $8 , %esp
# s i g n o s ( n2 , v2 )
# s i g n o s ( n3 , v3 )
movl $0 , %ebx
movl $1 , %eax
i nt $0x80
signos :
p u s h l %ebp
movl %esp , %ebp
s u b l $12 , %esp
p u s h l %ebx
movl $0 , 4( %ebp )
movl $0 , 8( %ebp )
movl $0 , 12( %ebp)
#
#
#
#
#
v o i d s i g n o s ( i nt n , i nt v )
S a l v a r a n t i g u o ba se p o i n t e r
E s t a b l e c e r e n l a c e dina mico
R e s e r v a r pa r a 3 va r l o n g
S a l v a r r e g s %ebx %e s i %edi
# I n i c i a l i z a r VarLoc1 a 0
# I n i c i a l i z a r VarLoc2 a 0
# I n i c i a l i z a r VarLoc3 a 0
88
CAPITULO 5. SUBRUTINAS
41
movl $0 , %ecx
# I n i ci a l i za r indice a 0
42
movl 12( %ebp ) , %ebx # Parametro2 ( v ) a %ebx
43 s i g n o s f o r :
44
cmpl 8( %ebp) , %ecx # Comp Parametro1 ( n ) y %ecx
45
jge s i g n o s e n d f o r
# Si hemos r e c o r r i d o v s a l i r
46
47
p u s h l %ecx
48
p u s h l ( %ebx, %ecx , 4 ) # < e s t o es v [ i ]
49
p u s h l $sd
50
call printf
# p r i n t f ( %d , , v [ i ] ) ;
51
a ddl $8 , %esp
52
po pl %ecx
53
54
cmpl $0 , ( %ebx, %ecx , 4 ) # cmp v [ i ] con 0
55 #
j e s i g n o s z e r o # == 0 # s i v [ i ] es 0 . . .
56
jg s i g n o s p o s i # > 0
# s i v [ i ] es > 0 . . .
57
j l signos nega # < 0
# s i v [ i ] es < 0 . . .
58 s i g n o s z e r o :
# v [ i ] es 0
59
i n c l 4( %ebp )
# VarLoc1++
60
jmp s i g n o s f o r i n c
# salto a fin iteracion
61 s i g n o s p o s i :
# v [ i ] es > 0
62
i n c l 8( %ebp )
# VarLoc2++
63
jmp s i g n o s f o r i n c
# salto a fin iteracion
64 s i g n o s n e g a :
# v [ i ] es < 0
65
i n c l 12( %ebp )
# VarLoc3++
66 #
jmp s i g n o s f o r i n c
# salto a fin iteracion
67 s i g n o s f o r i n c :
# f i n a l de i t e r a c i o n
68
i n c l %ecx
# i++ ( c o n t a d o r de b u c l e )
69
jmp s i g n o s f o r
# s a l to a p r i n c i p i o bucle
70 s i g n o s e n d f o r :
# f i n a l de b u c l e
71
72
p u s h l %ecx
73
p u s h l 12( %ebp)
# VarLoc3
74
p u s h l 8( %ebp )
# VarLoc2
75
p u s h l 4( %ebp )
# VarLoc1
76
pushl $s
77
call printf
# Imprimo l a s 3 Var l o c a l e s
78
a ddl $16 , %esp
79
po pl %ecx
80
81
po pl %ebx
# R e s t a u r a r %edi , %es i , %ebx
82
a ddl $12 , %esp
# L i b e r a r e s p a c i o Var L o c a l e s
83
po pl %ebp
# Desha cer e n l a c e dina mico
84
ret
# Reto r na r
89
EL CODIGO
hasta la linea 31: El main no tiene ning
un secreto. Invocamos tres
veces a signos con los tres vectores y finaliza la ejecuci
on.
linea 15: Estado de la pila antes de invocar signos:
@-
@+
n1
$v1
%esp
%ebx
VarLoc3
VarLoc2
VarLoc1
%ebp main
@ret
n1
$v1
%esp
%ebp
l36
l35
l35
l35
l33
l15
l14
l13
pushl %ebx
subl $12, %esp
subl $12, %esp
subl $12, %esp
push %ebp
call signos
pushl n1
pushl $v1
Antes
de
invocar
a
signos
salvaramos
los
registros %eax, %ecx, %ebx que usamos. En este caso ninguno.
Luego apilaramos los par
ametros con los que invocamos signos
empezando por el u
ltimo (l13 y l14).
Hacemos el call (l15) que hace que nos apile la direcci
on de
retorno (direccion a la que tenemos que saltar al hacer un ret)
y salte a la etiqueta de la subrutina signos.
Establecemos el enlace dinamico salvando el base pointer del
main (l33) y haciendo que el nuevo %ebp (el %ebp de signos)
apunte a la cima de la pila actual (l34).
Reservamos espacio para las 3 variables long (l35).
Salvamos los registros base e ndices que se modifican dentro de
signos, que en este caso, solo es el %ebx (l36).
CAPITULO 5. SUBRUTINAS
90
signos-12)
signos-8)
signos-4)
signos)
signos+4)
signos+8)
signos+12)
@ret a signos
( %ebx, %ecx,4)
%ecx
%ebx
VarLoc3
VarLoc2
VarLoc1
%ebp main
@ret a main
n1
$v1
%esp
l50
l49
l48
l36
l35
l35
l35
l33
l15
l14
l13
call printf
pushl ...
pushl %ecx
pushl %ebx
subl $12, %esp
subl $12, %esp
subl $12, %esp
push %ebp
call signos
pushl n1
pushl $v1
EL BOXEO
El ensamblador es como el boxeo porque todo va al reves:
Para reservar espacio en la pila se le resta el tama
no en bytes al %esp.
Y para liberar ese espacio, se le suma.
91
TEORIA
RESUMEN DE LLAMADA A SUBRUTINA
Subrutina1 llama a Subrutina2. El n
umero (1..14) ndica en que orden
suceden los pasos.
En el c
odigo de Subrutina 1:
1- Salvar los regs
2- Pasar los par
ametros
3- Llamar a la subrutina 2
12- Recoger resultado
13- Eliminar par
ametros
14- Restaurar regs
En el c
odigo de Subrutina2:
4- Establecer enlace din
amico
5- Reservar espacio para var locales
6- Salvar registros
(Ejecucion de la subrutina 2)
7- Devolver resultado
8- Restaurar regs
9- Liberar el espacio de var locales
10- Deshacer enlace din
amico
11- Retorno
92
5.9.
CAPITULO 5. SUBRUTINAS
Paso de par
ametros por referencia
5.9. PASO DE PARAMETROS
POR REFERENCIA
93
.data
s f u n c : . a s c i z f u n c : ( %d ) ( %d ) \n
CAPITULO 5. SUBRUTINAS
94
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
5.9. PASO DE PARAMETROS
POR REFERENCIA
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
swap :
p u s h l %ebp
movl %esp , %ebp
p u s h l %e s i
p u s h l %edi
movl 12( %ebp) , %ecx
p u s h l ( %ecx )
p u s h l %ecx
movl 8( %ebp ) , %eax
p u s h l ( %eax )
p u s h l %eax
p u s h l $sswap
call printf
a ddl $20 , %esp
movl
movl
movl
movl
movl
movl
8( %ebp ) , %eax
12( %ebp) , %ecx
( %eax ) , %e s i
( %ecx ) , %edi
%edi , ( %eax )
%es i , ( %ecx )
p u s h l %edi
p u s h l %e s i
p u s h l $sswap2
call printf
a ddl $12 , %esp
movl 12( %ebp) , %ecx
p u s h l ( %ecx )
p u s h l %ecx
movl 8( %ebp ) , %eax
p u s h l ( %eax )
p u s h l %eax
p u s h l $sswap
call printf
a ddl $20 , %esp
po pl %edi
po pl %e s i
po pl %ebp
ret
95
CAPITULO 5. SUBRUTINAS
96
Y la ejecuci
on de la versi
on en ensamblador nos demuestra que funciona
correctamente:
$ g c c g o t7 co mplex swa p t7 co mplex swa p . s
$ . / t7 co mplex swa p f u n c : ( 7 ) ( 7)
swap : ( @0xBFF870C8 : 7 ) (@0xBFF870BC: 7)
swap : ( e s i : 7 ) ( e d i : 7)
swap : ( @0xBFF870C8 : 7) (@0xBFF870BC : 7 )
f u n c : ( 7) ( 7 )
$
5.9.1.
movl %ebp , %e
s u b l $4 , %ebx
p u s h l %ebx # punter o a 4( %ebp)
movl %ebp , %ebx
a ddl $8 , %ebx
p u s h l %ebx # punter o a 8( %ebp )
c a l l swap
Equivaldra a:
1
2
3
4
5
l e a l 4( %ebp ) , %ebx
p u s h l %ebx
l e a l 8( %ebp ) , %ebx
p u s h l %ebx
c a l l swap
5.10.
5.10.1.
97
Programa que indica cuantas veces aparece cada letra del abecedario en una
cadena ascii. En el podemos ver como acceder a posiciones de vectores que se
han pasado como par
ametro o que son variables locales.
En este ejemplo podemos ver que OFFSET LETRAS es una constante que como valor tiene el offset o desplazamiento relativo de una variable a %ebp. Por
lo tanto lo que seria v[i] en C (donde v es una variable local). En ensamblador
sera OFFSET LETRAS( %ebp, %ecx,4). El 4 porque es un vector de longs.
C
odigo fuente 5.12: t7 cuento minusculas b.s
1 NUM LETRAS = 26
2 SIZE LETRAS = +4NUM LETRAS
3 OFFSET LETRAS = 4NUM LETRAS
4 .data
5 s i n t r o : . a s c i z Cuento l a s a p a r i c i o n e s de l e t r a s
m i n u s c u l a s . \n\n
6 s1 : . a s c i z ho la , muchas l e t r a s veo yo !
7 s2 : . a s c i z m u r c i e l a g o
8 s3 : . a s c i z 9 a a a a a a a a a s
9 s : . a s c i z %d
10 s c : . a s c i z %c
11 s r : . a s c i z \n\n
12 s l e t r a s : . a s c i z \ na b c d e f g h i j k l m n o p q
r s t u v w x y z \n
13 . t e x t
14 . g l o b a l main
15 main :
16
pushl $ s i n t r o
17
call printf
18
a ddl $4 , %esp
19
20
p u s h l $ s1
21
call l e t r as
22
a ddl $4 , %esp
23
24
p u s h l $ s2
25
call l e t r as
26
a ddl $4 , %esp
27
28
p u s h l $ s3
29
call l e t r as
30
a ddl $4 , %esp
CAPITULO 5. SUBRUTINAS
98
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
movl $0 , %ebx
movl $1 , %eax
i nt $0x80
letras :
p u s h l %ebp
movl %esp , %ebp
s u b l $SIZE LETRAS , %esp
p u s h l %ebx
movl $0 , %ecx
letras for0 :
cmpl $NUM LETRAS, %ecx
jge l e t r a s e n d f o r 0 # %ecx>=$NUM LETRAS
movl $0 , OFFSET LETRAS( %ebp, %ecx , 4 )
i n c l %ecx
jmp l e t r a s f o r 0
letras endfor0 :
movl 8( %ebp) , %ebx
movl $0 , %ecx
letras for1 :
cmpb $0 , ( %ebx, %ecx )
j e l e t r a s e n d f o r 1 # $0==( %ebx, %ecx )
p u s h l %ecx
p u s h l ( %ebx, %ecx )
pushl $sc
call printf
a ddl $8 , %esp
po pl %ecx
# p a s s a r de a a
movl $0 , %eax
movb ( %ebx, %ecx ) ,
# begin c o n t r o l a r
cmpb $ a , %a l
jl letras incfor1
cmpb $ z , %a l
jg l e t r a s i n c f o r 1
# end c o n t r o l a r
subb $ a , %a l
0 , de b a 1 , . . . , z a 25
%a l
que p a s s a s i ( %al< a | | %al> z )
# %al< a
# %al> z
99
letras incfor1 :
i n c l %ecx
jmp l e t r a s f o r 1
letras endfor1 :
p u s h l %ecx
pushl $ s l e t r a s
call printf
a ddl $4 , %esp
po pl %ecx
movl $0 , %ecx
letras for2 :
cmpl $NUM LETRAS, %ecx
jge l e t r a s e n d f o r 2 # %ecx>=$NUM LETRAS
p u s h l %ecx
p u s h l OFFSET LETRAS( %ebp, %ecx , 4 )
pushl $s
call printf
a ddl $8 , %esp
po pl %ecx
i n c l %ecx
jmp l e t r a s f o r 2
letras endfor2 :
pushl $sr
call printf
a ddl $4 , %esp
po pl %ebx
movl %ebp , %esp
po pl %ebp
ret
$ g c c g o t 7 c u e n t o m i n u s c u l a s b t 7 c u e n t o m i n u s c u l a s b . s
$ ./ t7 cuento minusculas b
Cuento l a s a p a r i c i o n e s de l e t r a s m i n u s c u l a s .
ho la , muchas l e t r a s veo yo !
a b c d e f g h i j k l m n o p q r s t u v w x y z
3 0 1 0 2 0 0 2 0 0 0 2 1 0 3 0 0 1 2 1 1 1 0 0 1 0
murcielago
CAPITULO 5. SUBRUTINAS
100
a b c d e f g h i j k l m n o p q r s t u v w x y z
1 0 1 0 1 0 1 0 1 0 0 1 1 0 1 0 0 1 0 0 1 0 0 0 0 0
9 aaaaaaaaas
a b c d e f g h i j k l m n o p q r s t u v w x y z
9 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0
$
5.10.2.
( %ebx, %ecx,4)
Variante del mismo programa con direccionamientos del estilo ( %ebx, %ecx,4).
Lo que hacemos es poner en el registro base ( %ebx) la base del vector (que
es %ebp + OFFSET DEL VECTOR) y a partir de aqu la equivalencia de lo
que sera v[i] en C es ( %ebx, %ecx,4) en ensamblador.
C
odigo fuente 5.13: t7 cuento minusculasb.s
1 NUM LETRAS = 26
2 SIZE LETRAS = +4NUM LETRAS
3 OFFSET LETRAS = 4NUM LETRAS
4 .data
5 s i n t r o : . a s c i z Cuento l a s a p a r i c i o n e s de l e t r a s
m i n u s c u l a s . \n\n
6 s1 : . a s c i z ho la , muchas l e t r a s veo yo !
7 s2 : . a s c i z m u r c i e l a g o
8 s3 : . a s c i z 9 a a a a a a a a a s
9 s : . a s c i z %d
10 s c : . a s c i z %c
11 s r : . a s c i z \n\n
12 s l e t r a s : . a s c i z \na b c d e f g h i j k l m n o p q
r s t u v w x y z \n
13 . t e x t
14 . g l o b a l main
15 main :
16
pushl $ s i n t r o
17
call printf
18
a ddl $4 , %esp
19
20
p u s h l $ s1
21
call l e t r a s
22
a ddl $4 , %esp
23
24
p u s h l $ s2
25
call l e t r a s
101
a ddl $4 , %esp
p u s h l $ s3
call l e t r as
a ddl $4 , %esp
movl $0 , %ebx
movl $1 , %eax
i nt $0x80
letras :
p u s h l %ebp
movl %esp , %ebp
s u b l $SIZE LETRAS , %esp
p u s h l %ebx
p u s h l %e s i
movl $0 , %ecx
movl %ebp , %ebx
a ddl $OFFSET LETRAS, %ebx
letras for0 :
cmpl $NUM LETRAS, %ecx
jge l e t r a s e n d f o r 0 # %ecx>=$NUM LETRAS
movl $0 , ( %ebx, %ecx , 4 )
i n c l %ecx
jmp l e t r a s f o r 0
letras endfor0 :
movl 8( %ebp) , %e s i
movl $0 , %ecx
letras for1 :
cmpb $0 , ( %es i , %ecx )
j e l e t r a s e n d f o r 1 # $0==( %es i , %ecx )
p u s h l %ecx
p u s h l ( %es i , %ecx )
pushl $sc
call printf
a ddl $8 , %esp
po pl %ecx
# p a s s a r de a a 0 , de b a 1 , . . . , z a 25
movl $0 , %eax
movb ( %es i , %ecx ) , %a l
# b e g i n c o n t r o l a r que p a s s a s i ( %al< a | | %al> z )
cmpb $ a , %a l
CAPITULO 5. SUBRUTINAS
102
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
j l l e t r a s i n c f o r 1 # %al< a
cmpb $ z , %a l
jg l e t r a s i n c f o r 1 # %al> z
# end c o n t r o l a r
subb $ a , %a l
i n c l ( %ebx, %eax , 4 )
letras incfor1 :
i n c l %ecx
jmp l e t r a s f o r 1
letras endfor1 :
p u s h l %ecx
pushl $ s l e t r a s
call printf
a ddl $4 , %esp
po pl %ecx
movl $0 , %ecx
letras for2 :
cmpl $NUM LETRAS, %ecx
jge l e t r a s e n d f o r 2 # %ecx>=$NUM LETRAS
p u s h l %ecx
p u s h l ( %ebx, %ecx , 4 )
pushl $s
call printf
a ddl $8 , %esp
po pl %ecx
i n c l %ecx
jmp l e t r a s f o r 2
letras endfor2 :
pushl $sr
call printf
a ddl $4 , %esp
po pl
po pl
movl
po pl
ret
%e s i
%ebx
%ebp , %esp
%ebp
5.10.3.
103
C
odigo fuente 5.14: t7 fibonacci.s
1 SIZE = 20
2 .data
3 s : . a s c i z %d\n
4 .bss
5 .comm v , 4 SIZE , 4
6 .text
7 . g l o b a l main
8 main :
9 # f i b o n a c c i [0]=0
10
movl $0 , %ecx
11
movl $0 , v ( , %ecx , 4 )
12 # f i b o n a c c i [ 1 ] = 1
13
movl $1 , %ecx
14
movl $1 , v ( , %ecx , 4 )
15 # f i b o n a c c i [ 2 . . S I Z E ]
16
movl $2 , %ecx
17 loop :
18 # %ecx>=$SIZE
19
cmpl $SIZE , %ecx
20
jge endlo o p
21
22
push %ecx
23
push $v
24
call fibonacci
25
a ddl $4 , %esp
26
po pl %ecx
27
28
i n c l %ecx
29
jmp loop
30 endlo o p :
31
32 # p r i n t f t h e a r r a y
33
movl $0 , %ecx
34 l o o p p r i n t :
35
cmpl $SIZE , %ecx
CAPITULO 5. SUBRUTINAS
104
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
jge e n d l o o p p r i n t
p u s h l %ecx
p u s h l v(, %ecx , 4 )
pushl $s
call printf
a ddl $8 , %esp
po pl %ecx
i n c l %ecx
jmp l o o p p r i n t
endloop print :
# t h e end
movl $0 , %ebx
movl $1 , %eax
i nt $0x80
fibonacci :
p u s h l %ebp
movl %esp , %ebp
p u s h l %ebx
movl 12( %ebp) , %ecx
movl 8( %ebp) , %ebx
movl 4( %ebx, %ecx , 4 ) , %edx
a ddl 8( %ebx, %ecx , 4 ) , %edx
movl %edx , ( %ebx, %ecx , 4 )
po pl %ebx
po pl %ebp
ret
$ g c c g o t 7 f i b o n a c c i t 7 f i b o n a c c i . s
$ ./ t7 fibonacci
0
1
1
2
3
5
8
13
21
34
55
105
89
144
233
377
610
987
1597
2584
4181
$
5.10.4.
v( %esi, %ecx,4)
Variante del mismo programa que es mas simple por que no hay llamada a
subrutina y el vector no se pasa como par
ametro. Con lo cual podemos hablar
de v[a+i] con direccionamientos del estilo v( %esi, %ecx,4).
C
odigo fuente 5.15: t7 fibonacci 2.s
1 SIZE = 20
2 .data
3 s : . a s c i z %d\n
4 .bss
5 .comm v , 4 SIZE , 4
6 .text
7 . g l o b a l main
8 main :
9 # f i b o n a c c i [0]=0
10
movl $0 , %ecx
11
movl $0 , v ( , %ecx , 4 )
12 # f i b o n a c c i [ 1 ] = 1
13
movl $1 , %ecx
14
movl $1 , v ( , %ecx , 4 )
15 # f i b o n a c c i [ 2 . . S I Z E ]
16
movl $2 , %ecx
17
movl $ 4 , %e s i
18
movl $ 8 , %edi
19 loop :
20 # %ecx>=$SIZE
21
cmpl $SIZE , %ecx
22
jge endlo o p
23
24
movl v( %es i , %ecx , 4 ) , %edx
25
a ddl v( %edi, %ecx , 4 ) , %edx
26
movl %edx , v(, %ecx , 4 )
CAPITULO 5. SUBRUTINAS
106
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
i n c l %ecx
jmp loop
endlo o p :
# p r i n t f the array
movl $0 , %ecx
loop print :
cmpl $SIZE , %ecx
jge e n d l o o p p r i n t
p u s h l %ecx
p u s h l v(, %ecx , 4 )
pushl $s
call printf
a ddl $8 , %esp
po pl %ecx
i n c l %ecx
jmp l o o p p r i n t
endloop print :
# t h e end
movl $0 , %ebx
movl $1 , %eax
i nt $0x80
Captulo 6
Eplogo
Aprender siempre es un proceso doloroso y uno debe no solo aceptar, sino
abrazarse a ese dolor. Caminar contra esa resistencia que notamos. Como
deca un maestro Zen El obstaculo es el camino!.
En este libro he intentado disminuir el dolor y la resistencia de este proceso de
aprendizaje. Tambien he intentado hacer mas divertido el proceso. Porque uno
siempre aprende mas si se lo pasa bien. Pero ahora falta que vosotros le echeis
horas y jugueis (programeis) hasta que esto se os de bien.
Espero que os lo hayais pasado tan bien leyendo este libro como yo me lo
he pasado escribiendolo. Y que, con un poco de suerte, en breve seais expertos
programadores en el ensamblador del IA32.
107
108
CAPITULO 6. EPILOGO
HICE UN PROGRAMA
DE CIEN LINEAS
DE MAS
EN ENSAMBLADOR Y
A LA PRIMERA
COMPILO