Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador. Una guía práctica con ejemplos
Versión 1.0
Elaboró: Peniel Ruiz López Unistmo
10/17/2011
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Manual para la Implementación de Programas
que Integren los Lenguajes Java y Ensamblador. Una guía práctica con ejemplos.
Peniel Ruiz López.
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 2
Contenido 1. GENERALIDADES ............................................................................................ 3
1.1 INTRODUCCIÓN. ........................................................................................... 3
1.2 OBJETIVOS. ..................................................................................................... 3
1.3 A QUIEN VA DIRIGIDO EL MANUAL. ............................................................. 3
1.4 ACRÓNIMOS, ANGLICISMOS Y ABREVIATURAS EMPLEADAS. ..................... 3
2. EL LENGUAJE JAVA ........................................................................................ 4
2.1 ¿QUÉ ES JAVA? ................................................................................................ 4
3. EL LENGUAJE ENSAMBLADOR .............................................................. 4
3.1 ENSAMBLADOR .............................................................................................. 4
3.2 ENLAZADOR ................................................................................................... 4
3.3 GENERALIDADES DEL LENGUAJE .................................................................. 4
4. UTILIDAD DE LA PROGRAMACIÓN HÍBRIDA .............................. 5
4.1 CASO GENERAL ............................................................................................. 5
4.2 CASO PARTICULAR ........................................................................................ 5
6. ENTORNOS DE DESARROLLO ............................................................... 5
6.1 ECLIPSE ............................................................................................................ 5
6.2 NETBEANS ...................................................................................................... 6
6.3 MASM32 ........................................................................................................ 6
7. JAVA NATIVE INTERFACE ......................................................................... 6
7.1 INTRODUCCIÓN ............................................................................................ 6
7.2 ¿CUÁNDO USAR JNI? .................................................................................... 7
7.3 USANDO JNI PARA INVOCAR MÉTODOS NATIVOS ................................... 7
8. EJEMPLO DE JASM ........................................................................................ 10
8.1 INTRODUCCIÓN ......................................................................................... 10
8.2 ALGUNOS CONSEJOS ANTES DE COMENZAR ........................................ 10
8.3 PRERREQUISITOS ......................................................................................... 11
8.4 IMPLEMENTACIÓN DEL MÉTODO NATIVO EN LENGUAJE ASM ............ 12
8.5 COMPILACIÓN CON MASM32 ................................................................. 13
8.6 ENLACE ........................................................................................................ 13
8.7 EJECUCIÓN ................................................................................................... 13
9. FUNCIONES UTILES DE LA JNI ............................................................ 14
10. GLOSARIO DE TERMINOS .................................................................... 14
APENDICE A: LIBRERÍAS ......................................................................... 15
APENDICE B: MACROENSAMBLADOR ........................................... 18
APENDICE C: CONFIGURACION DE VARIABLES DE
ENTORNO .............................................................................................................. 21
BIBLIOGRAFÍA ..................................................................................................... 24
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 3
1. GENERALIDADES
1.1 Introducción.
Este manual surgió de la necesidad de transmitir el conocimiento adquirido
durante la realización del proyecto final de la materia de Lenguaje
Ensamblador.
El presente manual también pretende transmitir la estructura, conceptos e
información básica de la Interfaz Nativa de Java, con la finalidad de que
conociendo su funcionamiento, aquellos quienes sigan este manual, puedan
implementar sus programas de manera efectiva. Por lo mencionado, es de
suma importancia, leer el manual que se detalla a continuación antes y
durante el proceso de desarrollo.
El manual comienza explicando las ideas básicas acerca de la programación
híbrida y como sus aplicaciones, después de ello plantea una explicación del
funcionamiento de la Interfaz Nativa de Java, para luego ir explicando uno a
uno los pasos a seguir para llevar a cabo la implementación de funciones
nativas escritas en lenguaje Ensamblador.
Finalmente, se incluye un glosario con la terminología utilizada con la
finalidad de hacer más entendible este material.
1.2 Objetivos.
El principal objetivo del presente manual, es brindar información detallada
del proceso de integración de los lenguajes de programación antes
mencionados. Así mismo se pretende que el lector tenga una idea clara del
alcance que tiene dicha integración a través de la explicación ilustrada de
cada paso del proceso.
1.3 A quien va dirigido el manual.
Este manual va dirigido a cualquier entusiasta de la programación que tenga
nociones del lenguaje Java y esté familiarizado con conceptos del lenguaje
Ensamblador, así como académicos que quieran aplicar estos conocimientos
para la realización de sesiones prácticas relacionas con el tema o como
material meramente didáctico.
1.4 Acrónimos, anglicismos y abreviaturas empleadas.
API Application Programming Interface (Interfaz de
Programación de Aplicaciones).
ASM Assembly (Ensamblador).
COFF Common Object File Format (Formato de Archivo Objeto
Común)
IDE Integrated Development Environment
JASM Hibridación Java-ASM.
JDK Java Development Kit (Kit de Desarrollo de Java)
JNI Java Native Interface (Interfaz Nativa de Java).
LAN Local Area Network (Red de Área Local)
Linker Enlazador.
MP Microprocesador
pe. por ejemplo
Runtime Tiempo de ejecución.
TCP/IP Protocolo de Internet
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 4
2. EL LENGUAJE JAVA
2.1 ¿Qué es Java?
Java es un lenguaje de programación orientado a
objetos, desarrollado por Sun Microsystems en 1996 y
diseñado por James Gosling (Fig 1). El lenguaje en sí
mismo toma mucha de su sintaxis de C y C++, pero
tiene un modelo de objetos más simple y elimina
herramientas de bajo nivel, que suelen inducir a muchos
errores, como la manipulación directa de punteros o
memoria. Con respecto a la memoria, su gestión no es
un problema ya que ésta es gestionada por el propio
lenguaje y no por el programador.
Las aplicaciones Java están típicamente compiladas en
un bytecode, aunque la compilación en código máquina
nativo también es posible. En el tiempo de ejecución, el
bytecode es normalmente interpretado o compilado a
código nativo para la ejecución, aunque la ejecución
directa por hardware del bytecode por un procesador Java
también es posible.
El término Java fue acuñado en una cafetería frecuentada
por algunos de los miembros del equipo. Pero no está
claro si es un acrónimo o no, aunque algunas fuentes
señalan que podría tratarse de las iniciales de sus
creadores: James Gosling, Arthur Van Hoff, y Andy
Bechtolsheim. Otros abogan por el siguiente acrónimo,
Just Another Vague Acronym ("sólo otro acrónimo
ambiguo más"). La hipótesis que más fuerza tiene es la
que Java debe su nombre a un tipo de café disponible en
la cafetería cercana, de ahí que el icono de java sea una taza de café caliente
(Fig. 2).
3. EL LENGUAJE ENSAMBLADOR
3.1 Ensamblador
Los ensambladores como los descritos en este trabajo son como una
versión reducida y elemental de un compilador (pero que de ninguna
manera deben considerarse como tales), ya que lo único que tienen que
hacer es cambiar toda referencia simbólica por la dirección
correspondiente, calcular los saltos, resolver referencias y llamadas a otros
programas, y realizar el proceso de enlace. Los ensambladores son
programas destinados a realizar el ensamblado de un determinado código.
3.2 Enlazador
Para crear un programa ejecutable a partir de un código objeto se requiere
que se resuelvan las llamadas a otros programas y a los servicios del sistema
operativo, y agregar las rutinas o información de run−time para que el
programa pueda ser cargado a memoria y ejecutado. Este proceso es lo que
se conoce como proceso de liga, y se realiza a través de un ligador,
enlazador o linker que toma de entrada el código objeto y produce de salida
el código ejecutable.
3.3 Generalidades del Lenguaje
El único lenguaje que entienden los microprocesadores y
microcontroladores, es el código máquina formado por ceros y unos del
sistema binario.
El lenguaje ensamblador expresa las instrucciones de una forma más natural
al hombre a la vez que muy cercana al MP, ya que cada una de esas
instrucciones se corresponde con otra en código máquina. El lenguaje
ensamblador trabaja con nemónicos, que son grupos de caracteres
alfanuméricos que simbolizan las órdenes o tareas a realizar.
Figura 1 James Gosling, trabajó
durante 18 meses en Sand Hill Road en Menlo Park en el
desarrollo de Java
Figura 2 Los 4 primeros bytes de
los archivos .class que
genera el compilador de java, son en
hexadecimal, 0xCAFEBABE.
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 5
El programa escrito en lenguaje ensamblador se denomina código fuente
(*.asm). El programa ensamblador proporciona a partir de este fichero el
correspondiente código máquina, que suele tener la extensión (*.hex).
4. UTILIDAD DE LA PROGRAMACIÓN HÍBRIDA
4.1 Caso General
Hay ocasiones en que es necesario acceder a un nivel más bajo por razones
de operatividad e incluso de necesidad pe.
- Programas residentes que economicen memoria.
- Algoritmos rápidos para operaciones críticas.
- Parches (funciones no soportadas en LANs).
- etc.
La importancia del estudio de técnicas de hibridación de lenguajes radica en
la posible reutilización de código escrito en un lenguaje de bajo nivel o
cualquier otro.
4.2 Caso Particular
En el caso de Java y Ensamblador se pretende acceder a características no
disponibles para la tecnología Java pe. OpenCL, API gráfica, gestión de
nombres cortos en Windows, etc.
Igualmente a veces es conveniente implementar ciertas partes de la
aplicación en ASM por eficiencia pe.
- En el tratamiento de imágenes
- Transcodificadores de vídeo
- En casos en los que quiera cargar una librería nativa en un proceso
existente para evitar el costo de iniciar un nuevo proceso y cargar
la librería en el mismo
- Si se quiere implementar porciones de código en un lenguaje de
bajo nivel como ensamblador para disminuir el tiempo de
procesamiento. Por ejemplo, en aplicaciones que necesiten
renderizar gráficos 3D que requieren más tiempo de
procesamiento, habrá que escribir una librería para gráficos en
lenguaje ensamblador para tener un mejor rendimiento.
- etc.
También se intenta aprovechar las más poderosas características del
lenguaje ASM tales como la de explotar las capacidades del hardware de la
computadora a bajo nivel en forma eficiente, la velocidad a la que se
procesan las instrucciones codificadas en este lenguaje y su flexibilidad,
puesto que todo lo que puede hacerse con una máquina, puede hacerse en
el lenguaje ensamblador de esta máquina.
6. ENTORNOS DE DESARROLLO
6.1 Eclipse
Eclipse (Fig. 3) es un entorno de
desarrollo integrado de código
abierto multiplataforma para
desarrollar lo que el proyecto
llama "Aplicaciones de Cliente
Enriquecido", opuesto a las
aplicaciones "Cliente-liviano"
basadas en navegadores. Esta
plataforma, típicamente ha sido
usada para desarrollar entornos
de desarrollo integrados, como el
IDE de Java llamado Java Development Toolkit (JDT, que se entrega como
parte de Eclipse. Sin embargo, también se puede usar para otros tipos de
aplicaciones cliente, como BitTorrent o Azureus.
Figura 3 Spash Screen de la plataforma Eclipse.
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 6
6.2 Netbeans
NetBeans (Fig. 4) es un entorno de desarrollo integrado libre, hecho
principalmente para el lenguaje de programación Java. Existe además un
número importante de módulos para extenderlo. NetBeans IDE es un
producto libre y gratuito sin restricciones de uso.
Figura 4 Splash Screen del IDE Netbeans
El IDE NetBeans es un entorno de desarrollo integrado - una herramienta
para programadores pensada para escribir, compilar, depurar y ejecutar
programas. Está escrito en Java - pero puede servir para cualquier otro
lenguaje de programación. Existe además un número importante de
módulos para extender el IDE NetBeans. El IDE NetBeans es un producto
libre y gratuito sin restricciones de uso.
6.3 MASM32
El Microsoft Macro Assembler (MASM) es un ensamblador para la familia
x86 de microprocesadores. Fue producido originalmente por Microsoft
para el trabajo de desarrollo en su sistema operativo MS-DOS, y fue
durante cierto tiempo el ensamblador más popular disponible para ese
sistema operativo. El MASM soportó una amplia variedad de facilidades para
macros y programación estructurada, incluyendo construcciones de alto
nivel para bucles, llamadas a procedimientos y alternación (por lo tanto,
MASM es un ejemplo de un ensamblador de alto nivel). Versiones
posteriores agregaron la capacidad de producir programas para los sistemas
operativos Windows. MASM es una de las pocas herramientas de desarrollo
de Microsoft para las cuales no había versiones separadas de 16 bits y 32
bits.
El proyecto MASM32 [10] ha puesto juntos una muy impresionante librería
de programador, un repositorio de ejemplos de código, y una
extraordinaria documentación para los usuarios del MASM. MASM también
es soportado por una gran cantidad de páginas web y foros de discusión [9].
A pesar de la edad de este producto, sigue siendo uno de los
ensambladores en existencia mejor soportados.
7. JAVA NATIVE INTERFACE
7.1 Introducción
Java Native Interface es una característica de Java que permite incorporar
en aplicaciones Java código escrito en lenguaje C o C++, incluso ASM
(también Java, cuando es llamado desde aplicaciones escritas en C).JNI es
parte de la máquina virtual Java y permite invocaciones en ambos sentidos:
aplicaciones Java pueden invocar código nativo escrito en otro lenguaje y
viceversa.
Podemos usar JNI para escribir métodos nativos. Éstos tienen prototipo en
Java pero su implementación es hecha en otro lenguaje. Estas
implementaciones son pasadas a la máquina virtual a través de bibliotecas
nativas (.dll en Windows o .so en Linux) Ver tope de la Figura 3.
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 7
JNI también soporta una interfaz nativas permite incrustar una
implementación de la máquina virtual dentro de aplicaciones nativas (fondo
de la Figura 3). Aplicaciones nativas pueden enlazarse con una biblioteca
nativa que implementa la máquina virtual Java y luego usar la interfaz de
invocación para ejecutar componentes escritas en lenguaje Java. Así es
cómo un navegador escrito en C puede ejecutar applets en una máquina
virtual embebida
7.2 ¿Cuándo usar JNI?
Hay dos efectos secundarios del uso de JNI: Las aplicaciones que dependen
de métodos nativos dejan de correr en otros ambientes. Se hace necesario
re-hacer la biblioteca para cada ambiente. En segundo lugar se pierden
algunas características del lenguaje. Un mal comportamiento de un método
nativo afecta toda la aplicación. ¿Qué pasa si el método nativo genera fuga
de memoria? Como regla general se debe limitar el uso de métodos nativos
a un mínimo.
Se recomienda usar JNI cuando la aplicación requiere alguna característica
del host no accesible a través de la máquina virtual, cuando se desea
acceder a bibliotecas nativas, o cuando deseamos dar mayor velocidad a
porciones críticas del código.
Existen alternativas a JNI que implican comunicación entre procesos. Por
ejemplo, cuando usamos la clase Runtime para ejecutar procesos nativos en
forma concurrente. También podemos comunicar procesos vía TCP/IP, usar
tecnologías para distribuir objetos como la API de Java IDL. En todos estos
casos se sacrifica eficiencia por la necesaria comunicación entre procesos.
7.3 Usando JNI para invocar métodos nativos
En general el uso es simple, la mayor atención se debe poner en el paso de
parámetros entre el método en Java y su implementación en C. Los pasos a
seguir se resumen en la Figura 4.
Figura 6 Pasos para incluir métodos nativos en una aplicación Java.C
A través de un ejemplo simple veremos estos pasos uno a uno.
Crear una Clase que Implementa el método nativo.
• Archivo.java
Usar javac para compilar el programa.
• Archivo.class
Usar javah para generar arcivo encabezado.
• Archivo.h
Escribir la implementación C del metodo nativo.
• Archivo.c
Compilar y generar la biblioteca .(dll .so).
• Archivo.dll
Correr usando el interpréte java.
Aplicación Java y
Biblioteca
yy
Host
Implementación
de la Máquina
Virtual Java
JNI
Aplicación Nativa y
Biblioteca
yy Figura 5Usos de JNI, Aplicación Java más biblioteca puede correr sobre el host a través de la JVM, o una Aplicación Nativa más una biblioteca puede correr sobre el host.
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 8
1. Creación de una clase que declara un método nativo.
Abrimos nuestro editor de texto y pegamos el siguiente segmento de
código, a continuación lo guardamos con el nombre de la clase (Hello.java).
//Fichero: Hello.java
class Hello {
public native void sayHello();
static {
System.loadLibrary("hello");
}
public static void main(String[] args) {
Hello h = new Hello();
h.sayHello ();
}
}
En este ejemplo el modificador native del método sayHello le indica al
compilador de java que dicho método estará escrito en otro lenguaje
(llamado nativo) por lo que buscara la implementación asociada a este en
la(s) librería(s) ubicadas en el mismo directorio de nuestro archivo .java
2. Compilación usando el comando javac en MS-DOS.
Abrimos una instancia de la terminal y valiéndonos del comando cd nos
posicionamos en el directorio de trabajo (en donde se encuentra el archivo
.java previamente guardado) a continuación tecleamos la siguiente
instrucción:
Se generará un archivo llamado Hello.class.
3.- Creación del encabezado del método nativo.
En la misma instancia de consola tecleamos:
Se generará un fichero de cabecera llamado Hello.h. En este caso su
contenido esencial es:
La implementación C del método tiene un nombre que alude al código Java
de origen, nombre de la clase y nombre del método. Aun cuando el método
Java no tiene argumentos, su implementación considera dos. El primer
argumento de toda implementación de método nativo es un puntero a
JNIEnv, el segundo es una referencia al objeto Hello mismo (como el
puntero a this en C++).
4.- Implementación del método nativo.
Procedemos a implementar la funcionalidad del método nativo en un
archivo asociado al lenguaje apropiado, en nuestro ejemplo, lenguaje C, de
la siguiente manera.
#include <jni.h>
#include <stdio.h>
#include "Hello.h"
// JNIEXPORT y JNICALL corresponde a macros requeridas, en medio
el tipo retornado.
JNIEXPORT void JNICALL Java_Hello_sayHello (JNIEnv *env, jobject
obj) {
printf("Hello World!\n");
return;
}
int main() {
return 0;
}
JNIEXPORT void JNICALL Java_Hello_sayHello(JNIEnv *, jobject);
C:\> javah -jni Hello
C:\>javac Hello.java
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 9
No olvidar codificar la función main si trabajamos en el ambiente Dev-C++
ya que de otra forma no se compilara correctamente el fichero.
Se debe incluir encabezados según la implementación que hagamos. El
código es C convencional.
5.- Compilación del código C y creación de la biblioteca de
ligado dinámico.
Esto depende del sistema operativo nativo. En este caso será Windows.
Para el compilador gcc (el mismo usado por Dev-C++) primero creamos el
archivo objeto, la sintaxis es:
El parámetro -I agrega dos rutas a nuestro rutas de inclusión estándar, para
que el compilador pueda accesar a los archivos cabecera de la JNI. Las rutas
son <java install dir>\include y <java install dir>\include\win32, donde <java
install dir> es reemplazado por el directorio en el que se encuentra
instalado el JDK. Si la ruta contiene espacios debera ser rodeada por
comillas dobles por claridad (para el lector y el compilador).
Una vez hecho esto procedemos a escribir un archivo de definiciones (.def)
especificando las funciones que serán exportadas, este fichero es usado por
el compilador al momento de crear la dll y tiene el siguiente
formato:
En nuestro caso el contenido seria
este:
Una vez creado este archivo procedemos a ejecutar lo siguiente para la
creación de la dll.
Este otro comando funciona con Microsoft Visual C++ 5.0 y genera la
librería dinámica directamente:
Con otros compiladores puede ser diferente; el lector deberá consultar el
manual del compilador para conocer los parámetros apropiados.
Como resultado en cualquiera de los casos se generará la biblioteca hello.dll
Si se quiere indicar al compilador donde se encuentran los ficheros
cabecera y las bibliotecas, se deben fijar dos variables de entorno (refiérase
al apéndice C para más información acerca de las variables de entorno):
Donde %JAVAHOME% es el directorio donde se ha instalado la plataforma
Java 2, o alguna versión del JDK.
6.- Ejecución de la aplicación
Podemos ejecutar la aplicación mediante la siguiente instrucción:
C:\>java HelloWorld
C:\>SET INCLUDE = %JAVAHOME%\include;%INCLUDE% C:\>SET LIB = %JAVAHOME%\lib;%LIB%
cl Hello.c -Fhello.dll -LD
gcc -shared -o hello.dll hello.o hello.def
EXPORTS Java_Hello_sayHello
EXPORTS < nombre_ función_1> < nombre_función_2> … < nombre_función_n>
gcc -c -I"C:\jdk1.x\include" -I"C:\jdk1.x\include\win32" -o hello.o Hello.c
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 10
El resultado de hacer esto sería:
Java también admite especificar el directorio de la biblioteca con la opción -
Djava.library.path, en este caso:
8. EJEMPLO DE JASM
8.1 Introducción
Como hemos visto en ejemplos anteriores resulta ser relativamente sencillo
lograr la correcta hibridación de aplicaciones Java con C como lenguaje
nativo, pero nuestro objetivo es el de lograrlo usando un enfoque distinto,
me refiero a utilizar ASM como lenguaje nativo.
Recurrir a los libros en busca de ejemplos concretos y bien detallados de
JASM es meramente una pérdida de tiempo pues en lo que a mí respecta,
no hay.
Buscando en Internet se pueden encontrar algunos (escasos) ejemplos de
JASM el principal inconveniente de estos es que no profundizan mucho en
el tema siendo ejemplos meramente triviales, otro de los inconvenientes es
que en su mayoría están en Ingles y por lo tanto se requiere de una
esfuerzo más grande el comprenderlos a cabalidad.
Es por esto que escribo este manual, para que aquellos a quienes les
interese JASM encuentren la pauta que buscan para desarrollar un camino
favorable en sus proyectos ya sea personales, profesionales o académicos.
Espero poder mejorar continuamente este material incorporando nuevos
conceptos y corrigiendo posibles fallas en él, así como recibir consejos y
sugerencias acerca de este documento.
El Autor.
8.2 Algunos Consejos Antes de Comenzar
Primero que nada sugiero revisar detenidamente cada uno de los apéndices,
en los que se incluye información útil acerca del entorno general en el que
trabajaremos.
Se sugiere la el uso de un makefile, o archivo por lotes ejecutable (.bat) es
decir un archivo conteniendo en orden los comandos necesarios para la
compilación y enlace de nuestro proyecto. Para este ejemplo el archivo por
lotes seria:
Guardamos como make.bat
@ECHO OFF Indica que solo se mostrarán los resultados de los
comandos usados en este archivo.
Nos aseguramos que no exista un archivo previo con el nombre
Operadores.obj u OperadoresDLL.dll
IF EXISTS Operadores.obj DEL Operadores.obj
IF EXISTS OperadoresDLL.dll DEL OperadoresDLL.dll
Borramos los archivos que no necesitaremos una vez que se haya
compilado correctamente nuestro código.
DEL Operadores.obj
DEL Operadores.exp
/nologo Evita la impresión los logotipos del ml y link.
@ECHO OFF CD "Desktop" javac Calculadora.java javah -jni Calculadora IF EXISTS Operadores.obj DEL Operadores.obj IF EXISTS OperadoresDLL.dll DEL OperadoresDLL.dll ml /c /coff /Cp /nologo Operadores.asm link /DLL /NOENTRY /subsystem:windows /nologo /DEF:OperadoresDLL.def Operadores.objOperadores.obj DEL Operadores.obj DEL Operadores.exp java Calculadora PAUSE
C:\>java -Djava.library.path = C:\directorio-de-la biblioteca\ HelloWorld
Hello World!
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 11
Más a delante daremos una explicación de los parámetros de los comandos
ml y link.
Es importante recordar que:
I. Hay que tener cuidado con el uso de los registros del MP ya que
podría haber inconvenientes con el sistema en el que estemos
desarrollando aplicaciones.
II. Avanzaremos a partir del paso “4. Implementación del método
nativo.” del apartado anterior que es donde nuestro enfoque se
separa de este ejemplo.
III. El lenguaje ASM está íntimamente relacionado con la arquitectura
del MP con el que cuente nuestra computadora.
IV. Por lo anterior las aplicaciones pueden no ser exportables a otros
sistemas (al menos no sin hacer las correcciones adecuadas)
V. Es una buena práctica documentar cada módulo de nuestro
programa ya sea en Java o ASM.
VI. Otra buena práctica de programación en ASM es la de escribir
entre comentarios como se verían nuestras funciones si
estuviéramos programando en C o Java.
VII. Nada es impensable para la inteligencia humana.
8.3 Prerrequisitos
Se sugiere por practicidad, que se sigan los pasos descritos en el Apéndice
C para la configuración de las variables de entorno de Java y MASM32.
Abriremos ya sea nuestro editor de texto o IDE para escribir una clase
Java que debe verse como sigue:
public class Calculadora {
public native int sumar(int a, int b);
public native int restar(int a, int b);
public native int multiplicar(int a, int b);
public native int ElevarAlCuad(int a);
public native double RaizCuad(int a);
static {
System.loadLibrary("Operadores");
}
public static void main(String[] args) {
int x = 11;
int y = 6;
Calculadora op = new Calculadora();
int resultadoInt = op.sumar(x, y);
System.out.println("El resultado de la suma es: " +
resultadoInt);
resultadoInt = op.restar(x, y);
System.out.println("El resultado de la resta es: " +
resultadoInt);
resultadoInt = op.multiplicar(x, y);
System.out.println("El resultado de la multiplicación
es: " + resultadoInt);
resultadoInt = op.ElevarAlCuad(x);
System.out.println("El resultado de la elevar x al
cuadrado es: " + resultadoInt);
double resultado = op.RaizCuad(y);
System.out.println("La raiz cuadrada de 'y' es: " +
resultado);
}
}
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 12
En este ejemplo crearemos una calculadora con operaciones aritméticas
básicas. Primero llevaremos a cabo los 3 primeros pasos del ejemplo del
apartado anterior sobre esta clase.
8.4 Implementación del Método Nativo en Lenguaje ASM
Una vez que tengamos el fichero cabecera, procedemos a crear el archivo
de definiciones como sigue:
No olvidemos que aqui hay que hacer referencia a TODOS los métodos
nativos que queramos que se incluyan en la librería dinámica, ya que un
posible error sería que nustro programa no encuentre algún método no
considerado en este fichero.
Lo guardamos con el nombre de OperadoresDLL.def y a continuación
escibiremos nuetra implementación de estas funciones:
.686
.model flat,stdcall
.code
Java_Calculadora_sumar proc JNIEnv:DWORD, jclass:DWORD, x:DWORD,
y:DWORD
mov eax, x
mov ebx, y
add eax, ebx ;sumamos el contenido del registro eax al
de ebx
ret
Java_Calculadora_sumar endp
Java_Calculadora_restar proc JNIEnv:DWORD, jclass:DWORD,
x:DWORD, y:DWORD
mov eax, x
mov ebx, y
sub eax, ebx ;restamos el contenido del registro eax al
de ebx
ret
Java_Calculadora_restar endp
Java_Calculadora_multiplicar proc JNIEnv:DWORD, jclass:DWORD,
x:DWORD, y:DWORD
mov eax, x
mov ebx, y
mul ebx ;multiplicamos el contenido del registro ebx con
el registro por default eax
ret
Java_Calculadora_multiplicar endp
Java_Calculadora_ElevarAlCuad proc JNIEnv:DWORD, jclass:DWORD,
x:DWORD
PUSH x
PUSH x
PUSH jclass
PUSH JNIEnv
call Java_Calculadora_multiplicar ;elevamos al cuadrado
x valiéndonos del procedimiento Java_Calculadora_multiplicar
ret
Java_Calculadora_ElevarAlCuad endp
Java_Calculadora_RaizCuad proc JNIEnv:DWORD, jclass:DWORD,
x:DWORD
LOCAL S:REAL4 ; Declaramos una variable de ambito local
; y de tipo real de 4 bytes
; Funciones del Coprocesador Matemático
fild x ; Cargamos el valor del parametro x
fsqrt ; Hallamos la raiz cuadrada
fst S ; Almacenamos el resultado en S
mov eax, S ; Transferimos el contenido de S a eax
ret
Java_Calculadora_RaizCuad endp
END
.686 Define el conjunto de instrucciones que estaremos usando, aquí 80386
.model flat Indica que estaremos usando un tipo de memoria protegida
plana de 32bits que es el que usa el 686 (Pentium Dual Core)
stdcall Define el orden en que se van a pasar los parámetros (izquierda a
derecha o derecha a izquierda)
EXPORTS Java_Calculadora_sumar Java_Calculadora_restar Java_Calculadora_multiplicar Java_Calculadora_ElevarAlCuad
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 13
Java_Calculadora_sumar proc JNIEnv:DWORD, jclass:DWORD,
x:DWORD, y:DWORD
Aquí podemos ver la declaración de nuestro procedimiento donde se define
que se llamará Java_Operaciones_sumar, tal como lo vimos antes, y tendrá
cuatro parámetros. Aquí podemos nombrar a nuestros parámetros como
nos plazca, pero debemos tener en cuenta el orden en el que aparecen y el
tipo de dato, en nuestro caso los parámetros que nos importan se llaman x
e y.
El resto es el código ASM que realizará la tarea que deseamos, en este caso
sumar x e y.
ret Al terminar de ejecutarse nuestro código, se hará un return que
devolverá el contenido del registro por default eax, por eso no se indica
explícitamente. El valor del registro eax será el que capturará el programa
Java y lo mostrará en la salida de pantalla.
Como habremos podido notar las funciones en ASM tienen una estructura
parecida una función en C, las ultimas 2 que hemos implementado son de
interés particular pues en la función Java_Calculadora_ElevarAlCuad
nos valemos de la función Java_Calculadora_multiplicar para calcular el
valor de retorno, Nótese que los parámetros son apilados en orden inverso
antes de realizar la llamada a la función mediante la directiva call.
La siguiente función a estudiar es Java_Calculadora_RaizCuad en ella
hacemos uso de una poderosa herramienta de cálculo matemático: El
Coprocesador, a través de la instrucción fild almacenamos el valor de x en
la pila del coprocesador, a continuación hallamos su raíz cuadrada por
medio de la instrucción fsqrt, luego salvamos este valor en S, una variable
local previamente definida que es del tipo apropiado para almacenar valores
de punto flotante con doble precisión después movemos el contenido de S
al registro eax para ser retornado a la clase Calculadora para su
despliegue.
Guardamos este código como Operadores.asm
8.5 Compilación con MASM32
Procederemos a compilar el código ASM con el ensamblador MAS32 (Si aún
no se han definido las variables de entorno para el comando ml refiérase al
Apéndice C donde se detallan los pasos a seguir para configurar el entorno
Java y MASM32. Luego entonces escribimos en consola:
ml Es el programa de MASM32 para crear el objeto
/c le indicamos que solo habrá de ensamblar el archivo .obj
/coff para indicar que el objeto ensamblado tendrá el formato COFF
/Cp indicará a MASM32 que será sensible a mayúsculas y minúsculas de los
identificadores que se usen
8.6 Enlace
El comando a ejecutar es el siguiente:
link el programa (enlazador) que usaremos para generar el .dll
/DLL indica que lo que queremos generar es un DLL
/NOENTRY para poder evitar algunos posibles errores no capturados
/SUBSYSTEM:windows indicamos que el ejecutable es para windows
/DEF:archivo.def y el archivo de definición que se usará para la exportación
Finalmente indicamos el nombre de nuestro archivo ensamblado,
Operadores.obj
8.7 Ejecución
Ejecutamos el programa así:
Java Hello
link /DLL /NOENTRY /subsystem:windows /DEF:OperadoresDLL.def Operadores.obj
ml /c /coff /Cp Operadores.asm
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 14
Y el resultado será este:
9. FUNCIONES UTILES DE LA JNI
10. GLOSARIO DE TERMINOS
Applet - Un applet es un componente de una aplicación que se ejecuta en el contexto de otro
programa, por ejemplo un navegador web. El applet debe ejecutarse en un contenedor, que lo
proporciona un programa anfitrión, mediante un plugin, o en aplicaciones como teléfonos
móviles que soportan el modelo de programación por 'applets'.
Bytecode - El bytecode es un código intermedio más abstracto que el código máquina.
Habitualmente es tratado como un fichero binario que contiene un programa ejecutable similar a
un módulo objeto, que es un fichero binario producido por el compilador cuyo contenido es el código objeto o código máquina.
Compilación - Es el proceso por el cual se traducen las instrucciones escritas en un
determinado lenguaje de programación a lenguaje máquina. Además de un traductor, se pueden
necesitar otros programas para crear un programa objeto ejecutable. Un programa fuente se puede dividir en módulos almacenados en archivos distintos. La tarea de reunir el programa
fuente a menudo se confía a un programa distinto, llamado preprocesador. El preprocesador
también puede expandir abreviaturas, llamadas a macros, a proposiciones del lenguaje fuente.
Compilador - Un compilador es un programa informático que traduce un programa escrito
en un lenguaje de programación a otro lenguaje de programación, generando un programa
equivalente que la máquina será capaz de interpretar. Usualmente el segundo lenguaje es
lenguaje de máquina, pero también puede ser un código intermedio (bytecode), o simplemente
texto. Este proceso de traducción se conoce como compilación.
Consola - Interfaz de Línea de Comandos (CLI), por su acrónimo en inglés de Command
Line Interface (CLI), es un método que permite a las personas dar instrucciones a algún programa informático por medio de una línea de texto simple. Debe notarse que los conceptos
de CLI, Shell y Emulador de Terminal no son lo mismo, aunque suelen utilizarse como
sinónimos.
Depurar - Depuración de programas es el proceso de identificar y corregir errores de
programación. En inglés se le conoce como debugging, ya que se asemeja a la eliminación de
bichos (bugs), manera en que se conoce informalmente a los errores de programación.
Host - El término host es usado en informática para referirse a las computadoras conectados a
una red, que proveen y utilizan servicios de ella. Los usuarios deben utilizar hosts para tener
acceso a la red. En general, los hosts son computadores monousuario o multiusuario que ofrecen
servicios de transferencia de archivos, conexión remota, servidores de base de datos, servidores web, etc. Los usuarios que hacen uso de los hosts pueden a su vez pedir los mismos servicios a
otras máquinas conectadas a la red. De forma general un host es todo equipo informático que
posee una dirección IP y que se encuentra interconectado con uno o más equipos.
Biblioteca (Librería) - En ciencias de la computación, una biblioteca (del inglés library)
es un conjunto de subprogramas utilizados para desarrollar software. Las bibliotecas contienen
código y datos, que proporcionan servicios a programas independientes, es decir, pasan a formar
parte de éstos. Esto permite que el código y los datos se compartan y puedan modificarse de forma modular. Algunos programas ejecutables pueden ser a la vez programas independientes y
bibliotecas, pero la mayoría de éstas no son ejecutables. Ejecutables y bibliotecas hacen
referencias (llamadas enlaces) entre sí a través de un proceso conocido como enlace, que por lo general es realizado por un software denominado enlazador.
GNU/Linux - Es uno de los términos empleados para referirse a la combinación del núcleo
o kernel libre similar a Unix denominado Linux, que es usado con herramientas de sistema GNU. Su desarrollo es uno de los ejemplos más prominentes de software libre; todo su código
fuente puede ser utilizado, modificado y redistribuido libremente por cualquiera bajo los
términos de la GPL (Licencia Pública General de GNU, en inglés: General Public License) y otra serie de licencias libres.
OpenCL - OpenCL (Open Computing Language, en español lenguaje de computación
abierto) consta de una interfaz de programación de aplicaciones y de un lenguaje de programación. Juntos permiten crear aplicaciones con paralelismo a nivel de datos y de tareas
que pueden ejecutarse tanto en unidades centrales de procesamiento como unidades de
procesamiento gráfico. El lenguaje está basado en C99, eliminando cierta funcionalidad y extendiéndolo con operaciones vectoriales.
Renderizar
Runtime - Se denomina tiempo de ejecución (runtime en inglés) al intervalo de tiempo en el
que un programa de computadora se ejecuta en un sistema operativo. Este tiempo se inicia con la puesta en memoria principal del programa, por lo que el sistema operativo comienza a
ejecutar sus instrucciones. El intervalo finaliza en el momento en que éste envía al sistema
operativo la señal de terminación, sea ésta una terminación normal, en que el programa tuvo la posibilidad de concluir sus instrucciones satisfactoriamente, o una terminación anormal, en el
que el programa produjo algún error y el sistema debió forzar su finalización.
Subrutina - En computación, una subrutina o subprograma (también llamada procedimiento,
función o rutina), como idea general, se presenta como un subalgoritmo que forma parte del algoritmo principal, el cual permite resolver una tarea específica. Algunos lenguajes de
programación, como Visual Basic .NET o Fortran, utilizan el nombre función para referirse a
subrutinas que devuelven un valor.
Terminal - Un terminal, conocido también como consola es un dispositivo electrónico o
electromecánico de hardware, usado para introducir o mostrar datos de una computadora o de un
sistema de computación.
El resultado de la suma es: 17 El resultado de la resta es: 5 El resultado de la multiplicacion es: 66 El resultado de la elevar al cuadrado es: 121
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 15
Transcodificar - Se denomina transcodificar (del inglés transcoding) a la conversión
directa (de digital a digital) de un códec a otro. Puede ser con o sin pérdida de calidad,
dependiendo del codec usado.
Esta operación implica decodificar/descomprimir los datos originales a un formato crudo (RAW
data) intermedio (por ejemplo, PCM para audio o YUV para vídeo), de manera que los imite, y
luego recodificarlos para alcanzar el códec deseado.
Unix - Registrado oficialmente como UNIX®, es un sistema operativo portable, multitarea y
multiusuario; desarrollado, en principio, en 1969 por un grupo de empleados de los laboratorios
Bell de AT&T, entre los que figuran Ken Thompson, Dennis Ritchie y Douglas McIlroy.
APENDICE A: LIBRERÍAS
INTRODUCCIÓN
Según vamos haciendo programas de ordenador, nos damos cuenta que
algunas partes del código se utilizan en muchos de ellos. Por ejemplo,
podemos tener varios programas que utilizan números complejos y las
funciones de suma, resta, etc. son comunes.
USO DE LIBRERÍAS
Sería estupendo poder tener esas funciones en un directorio separado de
los programas concretos y tenerlas previamente compiladas, de forma que
podamos usarlas siempre que queramos.
Las ventajas enormes de esto son:
a. No tener que volver a escribir el código (o hacer copy-paste).
b. Ahorro de tiempo de compilación puesto que este código ya está
compilado. Además, ya sabemos que mientras hacemos un
programa, probamos y corregimos, hay que compilar entre muchas
y "muchas más" veces.
c. El código ya compilado estará probado y será fiable. No las
primeras veces, pero sí cuando ya lo hayamos usado en una gran
cantidad de programas distintos y le hayamos corregido los
errores.
La forma de hacer esto es la creación de librerías. Una librería es en esencia
una o más funciones que tenemos ya compiladas y preparadas para ser
utilizadas en cualquier programa que hagamos. Hay que tener el suficiente
cuidado cuando las creamos para no involucrar ninguna dependencia de algo
concreto de nuestro programa.
CÓMO TENEMOS QUE ORGANIZAR NUESTRO CÓDIGO
Para poder implementar nuestro código en una librería, necesitamos
organizarlo de la siguiente manera:
- Uno o más ficheros fuente .c con el código de nuestras funciones.
- Uno o más ficheros de cabecera .h con los tipos (typedefs, structs
y enums) y prototipos de las funciones que vayamos a reutilizar.
Como es usual, vamos a hacer un ejemplo. Los ficheros serían estos:
Fichero libreria1.h #ifndef _LIBRERIA_1_H
#define _LIBRERIA_1_H
int suma (int a, int b);
int resta (int a, int b);
#endif
Fichero libreria1.c int suma (int a, int b) {
return a+b;
}
int resta (int a, int b) {
return a-b;
}
Un detalle importante a tener en cuenta, son los #define del fichero de
cabecera (.h). Al hacer una librería, no sabemos en qué futuros programas
la vamos a utilizar ni cómo estarán organizados.
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 16
Pensemos en un futuro programa en el que hay un fichero de cabecera
fichero1.h que mediante la directiva #include hace referencia a nuestra
librería. Imaginemos que hay también un fichero2.h que también hace
inclusión de ella. Finalmente, con un pequeño esfuerzo más, imaginemos
que hay un tercer fichero3.c que hace incluye al fichero1.h y fichero2.h, es
decir, más o menos lo siguiente:
fichero1.h
#include <libreria1.h>
...
fichero2.h
#include <libreria1.h>
...
fichero3.c
#include <fichero1.h>
#include <fichero2.h>
...
Cuando compilemos fichero3.c, dependiendo de lo que haya definido en
libreria1.h, obtendremos un error. El problema es que al incluir fichero1.h, se
define todo lo que haya en ese fichero, incluido lo de libreria1.h. Cuando se
incluye fichero2.h, se vuelve a intentar definir lo contenido en libreria1.h, y se
obtiene un error de que esas definiciones están definidas dos veces.
La forma de evitar este problema, es meter todas las definiciones dentro de
un bloque #ifndef - #endif, (también conocido como bloque de
compilación condicional) con el nombre (_LIBRERIA_1_H en el ejemplo)
que más nos guste y distinto para cada uno de nuestros ficheros de
cabecera. Es habitual poner este nombre precedido de _, acabado en _H y
que coincida con el nombre del fichero de cabecera, pero en mayúsculas.
Dentro del bloque #ifndef - #endif, hacemos un #define de ese nombre
(no hace falta darle ningún valor, basta con que esté definido) y luego
definimos todos nuestros tipos y prototipos de funciones.
Cuando incluyamos este fichero por primera vez, _LIBRERIA_1_H no
estará definido, así que se entrará dentro del bloque #ifndef - #endif y se
definirán todos los tipos y prototipos de funciones, incluido el mismo
_LIBRERIA_1_H. Cuando lo incluyamos por segunda vez, _LIBRERIA_1_H
ya estará definido (de la inclusión anterior), por lo que no se entrará en el
bloque #ifndef - #endif, y no se redefinirá nada por segunda vez.
Es buena práctica hacer esto con todos nuestros .h, independientemente de
que sean o no para librerías. Si te fijas en algún .h del sistema verás que
tienes este tipo de cosas hasta aburrir. Por ejemplo, en /usr/include/stdio.h, lo
primero que hay después de los comentarios, es un #ifndef _STDIO_H.
LIBRERIAS ESTÁTICAS Y DINÁMICAS
Una librería estática es una librería que "se copia" en nuestro programa
cuando lo compilamos. Una vez que obtenemos el ejecutable de nuestro
programa, la librería no sirve para nada (es un decir, sirve para otros
futuros proyectos). Podríamos borrarla y nuestro programa seguiría
funcionando, ya que tiene copia de todo lo que necesita. Sólo se copia
aquella parte de la librería que se necesite. Por ejemplo, si la librería tiene
dos funciones y nuestro programa sólo llama a una, sólo se copia esa
función.
Una librería dinámica NO se copia en nuestro programa al compilarlo.
Cuando tengamos nuestro ejecutable y lo estemos ejecutando, cada vez que
el código necesite algo de la librería, irá a buscarlo a ésta. Si borramos la
librería, nuestro programa arrojará un error informándonos que no la
encuentra.
¿Cuáles son las ventajas e inconvenientes de cada uno de estos tipos de librerías?
Un programa compilado con librerías estáticas es más grande, ya que se
hace copia de todo lo que necesita.
Un programa compilado con librerías estáticas se puede llevar a otro
ordenador sin necesidad de llevarse las librerías.
Un programa compilado con librerías estáticas es, en principio, más rápido
en ejecución. Cuando llama a una función de la librería, la tiene en su código
y no tiene que ir a leer el fichero de la librería dinámica para encontrar la
función y ejecutarla.
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 17
cc -o miprograma miprograma.c -I<path1> -I<path2> ... -
L<path1> -L<path2> ... -Bdynamic -llibreria1 -llibreria2
Si cambiamos una librería estática, a los ejecutables no les afecta. Si
cambiamos una dinámica, los ejecutables se ven afectados. Esto es una
ventaja si hemos cambiado la librería para corregir un error (se corrige
automáticamente en todos los ejecutables), pero es un inconveniente si
tocar eso nos hace cambiar los ejecutables (por ejemplo, hemos añadido un
parámetro más a una función de la librería, los ejecutables ya hechos dejan
de funcionar).
¿Qué tipo de librería uso entonces?
Es como siempre una cuestión de compromiso entre las ventajas y los
inconvenientes. Para programas no muy grandes y por simplicidad, suelen
usarse librerías estáticas. Las dinámicas están bien para programas enormes
o para librerías del sistema, que como están en todos los ordenadores con
sistema operativo Linux, no es necesario andar llevándoselas de un lado a
otro.
En sistemas Windows las librerías estáticas suelen llamarse nombre.lib y
las dinámicas nombre.dll (librería de enlace dinámico), donde „nombre‟ es el
nombre de nuestra librería.
En sistemas Unix las librerías estáticas suelen llamarse libnombre.a y las
dinámicas libnombre.so, donde „nombre‟ es el nombre de nuestra librería,
esta característica es heredada por todas las plataformas Linux.
CREACION DE LIBRERÍAS DINÁMICAS
Para compilar los mismos ficheros, pero como librería dinámica, tenemos
que seguir los siguientes pasos:
En sistemas Unix/Linux
1. Compilar los archivos fuente, igual que antes, para obtener los
objetos.
2. Crear la librería con el comando ld. Las opciones para este
comando serían:
La opción -o liblibreria.so le indica el nombre que queremos dar a la
librería. La opción -shared le indica que debe hacer una librería y no
un ejecutable (opción por defecto). objeto1.o, objeto2.o... son los
ficheros objeto que queremos incluir en la librería.
3. Una vez generada la librería, hay que enlazar con ella nuestro
programa.
4. Indicar al programa la localización de la o las librerías dinámicas.
En general podemos realizar el segundo paso para uno o más archivos
objeto:
La librería depende de los archivos fuente, los cuales se compilan para
obtener los .o (habría que añadir además las opciones -I<path> que fueran
necesarias), se construye la librería con ld y se borran los objetos
generados. Se ha hecho depender la librería de los archivos fuente para que
se compile sólo si se cambia un archivo fuente. Si se hace depender de los
objetos, como al final son borrados, siempre se recompilaría la librería.
Makefile
liblibreria.so: objeto1.c objeto2.c...
cc -c -o objeto1.o objeto1.c
cc -c -o objeto2.o objeto2.c
...
ld -o liblibreria.so objeto1.o objeto2.o ... -shared
rm objeto1.o objeto2.o ...
ld -o liblibreria.so objeto1.o objeto2.o... -shared.
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 18
Para el tercer paso podemos ejecutar lo siguiente:
El comando es igual que el anterior de las librerías estáticas con la
excepción del -Bdynamic. Es bastante habitual generar los dos tipos de
librería simultáneamente, con lo que es bastante normal encontrar de una
misma librería su versión estática y su versión dinámica. Al compilar sin
opción -Bdynamic pueden tenerse los siguientes casos:
i. Existen liblibreria.a y liblibreria.so. Se toma por defecto liblibreria.a
ii. Sólo existe una de ellas. Se coge la que existe.
iii. No existe ninguna de ellas. Error.
La opción -Bdynamic cambia el primer caso, haciendo que se tome
liblibreria.so en vez de liblibreria.a. La opción -Bdynamic afecta a todas las
librerías que van detrán en la línea de compilación. Para volver a cambiar,
podemos poner -Bstatic en cualquier momento.
Por último hay que decirle al programa, mientras se está ejecutando, dónde
están las librerías dinámicas, puesto que las va a ir a buscar cada vez que se
llame a una función de ellas. Tenemos que definir la variable de entorno
LD_LIBRARY_PATH, en la que ponemos todos los directorios donde
haya librerías dinámicas de interés.
Siendo <path> los directorios en los que están las librerías dinámicas. Se ha
puesto el $LD_LIBRARY_PATH pata mantener su valor anterior y
añadirle los nuevos directorios.
APENDICE B: MACROENSAMBLADOR
TIPOS DE ENSAMBLADORES
Definición: Un ensamblador es un programa que traduce mnemónicos de
un procesador a su correspondiente lenguaje de máquina.
Por la forma en que trabajan existen dos tipos de ensambladores:
Ensambladores de línea. Son aquellos que reciben una sola línea de un
programa y la ensambla independientemente del resto del programa.
Ejemplo: el comando a del servicio debug de MS-DOS.
Ensambladores de archivo. Son aquellos que ensamblan todo un programa
almacenado en un archivo.
Por el tipo de información que manejan los ensambladores se dividen
también en:
Ensambladores propios. (Residentes) Ensamblan programas escritos en
lenguaje del procesador con el que trabaja la máquina. Ejemplo MASM.
Ensambladores cruzados. (Crossassembler) Ensamblan programas escritos en
lenguaje de un procesador diferente al de la computadora de trabajo, pero
no puede ejecutarse.
Macroensambladores. Ensambladores propios o cruzados que permiten
definición y expansión de MACROS.
FACILIDADES DE LOS ENSAMBLADORES DE ARCHIVO.
- Nos permite definir etiquetas (nombre que nos marca una
dirección importante)
- Nos permite reservar memoria con una etiqueta asignada.
- Nos permite ensamblar programas almacenados en archivos.
- Nos permite definir constantes.
- Nos permite dar números en diferentes bases.
- Nos permite evaluar expresiones aritméticas. Ejemplo: mov ax,
30+2
$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<path1>:<path2>:<path3>
$ export LD_LIBRARY_PATH
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 19
MASM
Recibe archivos ASCII editados en cualquier editor que contenga programas
en lenguaje ensamblador, tales archivos deben tener extensión .asm y con
una forma específica.
Algoritmo
→ Codificación en ensamblador
→ Editar (archivo.asm)
→ Ensamblar (masm archivo.asm)
→ Código máquina (archivo.obj)
→ Enlazar
→ Crear archivo ejecutable (archivo.exe)
→ Ejecutar.
El archivo objeto no se puede ejecutar porque no tiene la dirección de
memoria donde se ejecutará y será ligado.
PSEUDOINSTRUCCIONES
Definición. Una pseudoinstrucción es una instrucción para el programa
ensamblador, esto es, que solo se ejecuta en el momento de ensamblar,
además no generar código.
Pseudoinstrucciones para definir segmentos:
SEGMENT: Define el inicio de un nuevo segmento. Su formato es:
ENDS: Define el final de un segmento. Su formato es:
Los parámetros del SEGMENT son información para el enlazador:
Alineación: Define la dirección a partir de donde puede colocarse el
segmento:
PARA: La dirección inicial del segmento es un múltiplo de 16 (10h).
PAGE: La dirección inicial del segmento es donde empieza una página
(múltiplo de 100h).
WORD: La dirección inicial del segmento es una dirección par.
BYTE: EL segmento inicia donde sea.
Combinación: Define la forma en que el segmento puede combinarse con
otros segmentos para que se tenga el mismo nombre y clase.
Al omitirla el segmento es privado, es decir, no puede combinarse.
STACK: Segmento para usarse con el stack.
PUBLIC: Este segmento puede unirse con todos los segmentos del mismo y
la misma clase para formar una sola.
COMMON: Todos los segmentos del mismo nombre y clase se colocan a
partir de la misma dirección.
Cuando se tienen dos segmentos con el mismo nombre y clase y son
públicos, al ligar se unen en un solo segmento no importando que estén en
archivos distintos. Cuando se usa la psudoinstrucción COMMON van a
utilizar el mismo espacio de memoria, si son de diferente tamaño en
memoria, se toma el tamaño del mayor bloque.
Clase: Indica el tipo de datos que contiene el segmento, siempre se ponen
entre comillas y pueden definirse propios.
„DATA‟: Datos.
„CODE‟: Código.
„STACK‟: Pila.
nombre ENDS
nombre SEGMENT alineación combinación clase
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 20
Pseudoinstrucciones para reservar memoria y definir constantes:
DB: Sirve para reservar un byte en la memoria con un valor determinado.
Su formato es:
DW: Reserva un dato de dos bytes (una palabra) con un valor inicial. Su
formato es:
DD: Reserva un dato de cuatro bytes (doble palabra) con un valor inicial. Su
formato es:
DQ: Reserva un dato de ocho bytes (cuádruple palabra) con un valor inicial.
Su formato es:
DT: Reserva un dato de diez bytes con un valor inicial. Su formato es:
val1 [, val2,..., valn] representa una expresión formada por números en
cualquiera de las siguientes bases:
XXXXb Binaria
XXXXo Octal
XXXXd Decimal
XXXX Decimal
XXXXh Hexadecimal
También pueden ser etiquetas o expresiones aritméticas que involucren
etiquetas o bien cadenas de caracteres, entre apóstrofes.
EQU: Permite definir constantes. Su formato
es:
ORG: Define un desplazamiento inicial para ensamblar las siguientes líneas.
Su formato es:
Pseudoinstrucciones para definir procedimientos:
PROC: Define el inicio de una subrutina.
ENDP: Define el final de una subrutina.
El tipo de la subrutina puede ser:
NEAR: Cercano.
FAR: Lejano.
Al omitirlo se define por omisión de tipo NEAR.
Un ensamblador de archivo, revisa errores de sintaxis, es decir, revisa que
el programa esté bien escrito, más no que funcione.
Para poner comentarios dentro del programa se inician con un „;‟ y todo lo
que este a la derecha será un comentario sobre el mismo renglón.
La estructura del archivo quedaría:
nombre ENDP
nombre PROC tipo
ORG val
etiq EQU val
[nombre] DT val1 [, val2,..., valn]
[nombre] DQ val1 [, val2,..., valn]
[nombre] DD val1 [, val2,..., valn]
[nombre] DW val1 [, val2,..., valn]
[nombre] DB val1 [, val2,..., valn]
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 21
APENDICE C: CONFIGURACION DE VARIABLES
DE ENTORNO
Antes de comenzar a programar será oportuno asegurarse de tener algunas
variables entorno necesarias para poder ejecutar algunas instrucciones
desde la ventana de comandos sin la necesidad de almacenar el código
dentro de los directorios que contienen los comandos/programas que serán
invocados.
Pasos generales para agregar variables de entorno.
1. Presionar Windows + Pausa para abrir las propiedades del sistema.
2. En la ficha 'Opciones avanzadas' clic en el botón 'Variables de entorno'
(Ilustración 1).
Datos SEGMENT PARA ‘DATA’
; Definición de variables y constantes
Datos ENDS
Pila SEGMENT PARA STACK ‘STACK’
DW 100 DUP (0) ; Indica que se tiene que
; repetir la instrucción n-veces con el
; valor que aparece en los paréntesis
Pila ENDS
Codigo SEGMENT PARA ‘CODE’
ASSUME DS:Datos, CS:Codigo, SS:Pila, ES:NOTHING
; Sirve para indicarle al
; macroensamblador cuales
; segmentos son usados por los registros
subrutina1 PROC
; Código de la rutina uno
subrutina1 ENDP
…
subrutina-n PROC
; Código de la rutina-n
subrutina-n ENDP
; Programa principal
Main PROC FAR
PUSH DS ; Sirve para cuando se
; termine el programa
XOR AX, AX ; regrese al debug o al
; sistema operativo según sea el
; caso.
PUSH AX
MOV AX, Datos
; Actualiza los registros de segmentos de
;datos y extra
MOV DS, AX
MOV ES, AX
; Código del programa principal.
Main ENDP
Codigo ENDS
END Main ; Le indica al macroensamblador que
el ensamble terminó
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 22
a.
b.
Ilustración 1Ficha Opciones avanzadas de Propiedades del sistema.
a.Widows 2000,/XP b. Windows Vista/7
3. En la nueva ventana (Ilustración 2) haremos clic en el botón 'Nueva' de la
sección 'Variables del sistema'.
a.
b.
Ilustración 2Variables de entorno. a Windows 2000/XP b. Windows Vista/7
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 23
4. En la ventana emergente 'Nueva variable del sistema' (Ilustración 3)
poner el nombre de la variable y la ubicación de la carpeta de la variable.
a.
b.
Ilustración 3 Ventana para agregar Nueva variable de sistema a. Windows 2000/XP b. Windows Vista/7
En el campo „nombre‟ escribimos
En el campo „valor‟ tecleamos la ruta en la que se encuentra instalada
nuestra versión actual de JDK pe.:
Hacemos click en aceptar y ahora haremos lo mismo para el compilador
ASM:
Importante. No olvidar que los valores correspondientes a las rutas
pueden variar dependiendo de cómo hayamos llevado a cabo la instalación
del software mencionado.
Busque la variable „ClassPath‟ y proceda a editar su contenido agregando los
siguientes valores al final del campo „valor‟ (después del último punto y
coma „;‟)
(No olvidar el punto y coma „;‟ al final).
Hay que tener MUCHO CUIDADO DE NO BORRAR EL CONTENIDO
EXISTENTE EN ESTAS VARIABLES ya que esto ocasionará problemas con
otras aplicaciones que hagan uso de ellas.
A continuación edite la variable 'Path' agregándole lo siguiente al valor de la
variable:
Hacemos click en aceptar en esta y todas las ventanas abiertas, es
importante recordar que si ya tenemos una instancia de consola debemos
cerrar y volver a abrir para que estos cambios tengan efecto.
Para asegurarse que se han agregado correctamente las variables de
entorno, ejecutar las siguientes instrucciones:
Para verificar la versión de Java que está siendo usada
Para asegurarse que reconoce el comando correspondiente al compilador
de MASM32.
Dado que el comando no tiene una opción para mostrar solo la versión del
compilador, lo que se desplegará en la pantalla será un error que
previamente mostrará la versión del compilador.
Con la diagonal, como si fuese a agregar parámetros, para evitar que se
desplegue en la pantalla las opciones del linker.
De igual manera, se verá en la pantalla un error que previamente mostrará
la versión del enlazador.
C:\> link
C:\> ml
C:\> javac -version
;%JAVA_HOME%\bin;%MASM32%\bin
%JAVAPATH%\src.zip;%JAVAPATH%\lib\tools.jar;
nombre = MASM32, valor = C:\masm32
C:\Archivos de programa\Java\jdk1.x
JAVAPATH
Manual para la Implementación de Programas que Integren los Lenguajes Java y Ensamblador.
Page 24
En la Imagen 4 se puede apreciar el resultado de las ejecuciones una tras
otra.
Ilustración 4 Comprobación de las variables de entorno.
Si en vez de obtener los resultados esperados como se observan en la
imagen se lanza alguno de los siguientes errores:
Entonces probablemente se ha agregado incorrectamente la variable de
entorno y deberá ser necesario volver sobre los pasos para hallar el error.
BIBLIOGRAFÍA
[1] Invocar programas ASM desde java paso a paso (versión PDF)
Autor: Jorge Ruiz Aquino
[2] Using the Java Native Interface
Author: Christopher Batty
Department of Computer Science, University of Manitoba, Winnipeg,
Manitoba, Canada
[3] Lenguaje ensamblador para microprocesadores Intel 80xx/80x86
Autor: M.C. Eduardo René Rodríguez Ávila.
[4] Java(TM) Native Interface: Programmer's Guide and Specification (Java
Series) by Sheng Liang 1999
[5] Java Native Interface: Programmer's guide and specification,
http://java.sun.com/docs/books/jni/html/jniTOC.html
[6] Wikipedia, Java Lenguaje de Programación, NetBeans, Eclipse, etc.
http://wikipedia.org
[7] Invoking Assembly Language Programs from Java
http://today.java.net/pub/a/today/2006/10/19/invoking-assembly-language-
from-java.html
[8] Pagina de descarga de MASM32 http://www.masm32.com/masmdl.htm
[9] Foro de MASM32 http://www.masmforum.com
[10] Pagina del proyecto MASM32 http://www.movsd.com/
"javac" no se reconoce como un comando interno o externo,
programa o archivo por lotes ejecutable.
"ml" no se reconoce como un comando interno o externo,
programa o archivo por lotes ejecutable.
"link" no se reconoce como un comando interno o externo,
programa o archivo por lotes ejecutable.