¿es .net una tecnología nueva?

24
¿Es .NET una tecnología nueva? Daniel Gayo Avello 1 ¿Es .NET una tecnología nueva? Daniel Gayo Avello Departamento de Informática Universidad de Oviedo C/Calvo Sotelo s/n 33007 Oviedo e-mail: [email protected] Resumen ...pero lo que pasará es lo que ya pasó, y todo lo que se hará ha sido ya hecho. ¡No hay nada nuevo bajo el sol! Si algo sucede y te dicen: "¡Mira, esto es nuevo!" no es así; las cosas que observan nuestros ojos ya pasaron en los siglos anteriores. Nadie se acuerda de las cosas de antaño: será lo mismo con los asuntos actuales, y de todo lo que pueda ocurrir en el futuro un día nadie más se acordará. Ecl. 1, 9-11 Microsoft ha presentado recientemente 1 el lenguaje C# y la plataforma .NET; se ha hablado mucho sobre ambos, su similitud con Java y las intenciones de Microsoft ocultas tras esta tecnología; por supuesto, Microsoft rechaza cualquier comentario que pretenda asimilar .NET o C# con Java. Hay que decir que ambas posturas aciertan en parte y, en parte también, se equivocan. Acusar a Microsoft de duplicar Java y su plataforma demuestra una actitud miope y simplista; negar las similitudes entre ambas tecnologías en lugar de señalar importantes disimilitudes manifiesta poca imaginación además de un intento de “ocultar” el poco romántico origen de la nueva tecnología. Como trataremos de justificar, C# y .NET no surgen como respuesta a Java puesto que el núcleo inicial de la tecnología es tan antiguo como el propio Java; sin embargo, ese núcleo inicial no surgió en Microsoft sino que, ¡ay!, fue comprado, he ahí el poco romántico origen. Describiremos esa tecnología original, especularemos sobre su evolución dentro de Microsoft y la relación existente entre su desarrollo, diversos proyectos y la tecnología finalmente presentada. ¿Será, entonces, este artículo una oda a los padres fundadores? Nada más lejos de nuestra intención pues, como se verá, nada nuevo propusieron puesto que alguna de las ideas verdaderamente originales, según nuestro propio y miope punto de vista, ya ha cumplido las cuatro décadas. 1. Introducción En febrero de 1994 un grupo de investigadores de la Universidad de California en Berkeley funda Colusa Software, apareciendo un año después las primeras versiones beta de su principal producto, Omniware, un sistema que permitía la generación y ejecución de código móvil en Internet. 1 La versión final de este trabajo se terminó el 3 de mayo de 2001. Omniware proporcionaba, como el resto de métodos de código móvil, portabilidad y seguridad: los mismos módulos Omniware, una vez compilados, podían ejecutarse en múltiples plataformas; por otro lado, el acceso de dichos módulos a los recursos del host sobre el que se ejecutaban podía controlarse de forma precisa. Además de las cualidades anteriores, Omniware tenía dos características que lo diferenciaban de los sistemas existentes hasta la fecha: Por un lado, empleaba una técnica conocida como software fault isolation (aislamiento de fallos por software ) o SFI que permitía la ejecución segura de lenguajes inseguros, como por ejemplo C y C++; esta característica posibilitaría, en principio, la utilización de cualquier lenguaje de programación tradicional para el desarrollo de código móvil. Por otro, Omniware era más rápido que el resto de soluciones disponibles en la época (fundamentalmente Java) puesto que el código móvil compilado no era interpretado en el host destino sino completamente traducido a código máquina nativo y, posteriormente, ejecutado. Estas cuatro características (portabilidad, seguridad, independencia del lenguaje de programación y rendimiento adecuado) situaban a Omniware en una posición inmejorable para convertirse en un sustrato universal para la programación en el web [LUC95]. 2. Colusa Omniware Omniware estaba constituida, principalmente, por dos componentes: un entorno integrado de desarrollo y un entorno de ejecución. El primero era, básicamente, un compilador de C/C++ que generaba módulos Omniware mientras que el segundo de los componentes proporcionaba los mecanismos necesarios para la carga de los módulos, su traducción a código máquina nativo y su posterior ejecución. Estos dos elementos sustentaban las cuatro características fundamentales de Omniware: Portabilidad: el compilador traducía C/C++ a código para una máquina abstracta, OmniVM, independiente de la plataforma destino; este código intermedio era traducido a código nativo por el entorno de ejecución, que sí cambiaba entre plataforma y plataforma. Seguridad: el acceso de cada módulo Omniware a los recursos del host se controlaba de forma precisa mediante una serie de permisos de acceso. Además, gracias a la SFI, Omniware garantizaba la ejecución segura de módulos externos aún cuando pudieran presentar fallos. Independencia del lenguaje: Omniware no introducía un nuevo lenguaje de programación sino que se basaba en la utilización de C/C++. La máquina

Upload: others

Post on 23-May-2022

6 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

1

¿Es .NET una tecnología nueva?

Daniel Gayo Avello Departamento de Informática

Universidad de Oviedo C/Calvo Sotelo s/n 33007 Oviedo

e-mail: [email protected]

Resumen

...pero lo que pasará es lo que ya pasó, y todo lo que se hará ha sido ya hecho. ¡No hay nada nuevo bajo el sol!

Si algo sucede y te dicen: "¡Mira, esto es nuevo!" no es así; las cosas que observan nuestros ojos ya pasaron

en los siglos anteriores.

Nadie se acuerda de las cosas de antaño: será lo mismo con los asuntos actuales, y de todo lo que pueda

ocurrir en el futuro un día nadie más se acordará.

Ecl. 1, 9-11

Microsoft ha presentado recientemente1 el lenguaje C# y la plataforma .NET; se ha hablado mucho sobre ambos, su similitud con Java y las intenciones de Microsoft ocultas tras esta tecnología; por supuesto, Microsoft rechaza cualquier comentario que pretenda asimilar .NET o C# con Java.

Hay que decir que ambas posturas aciertan en parte y, en parte también, se equivocan. Acusar a Microsoft de duplicar Java y su plataforma demuestra una actitud miope y simplista; negar las similitudes entre ambas tecnologías en lugar de señalar importantes disimilitudes manifiesta poca imaginación además de un intento de “ocultar” el poco romántico origen de la nueva tecnología.

Como trataremos de justificar, C# y .NET no surgen como respuesta a Java puesto que el núcleo inicial de la tecnología es tan antiguo como el propio Java; sin embargo, ese núcleo inicial no surgió en Microsoft sino que, ¡ay!, fue comprado, he ahí el poco romántico origen.

Describiremos esa tecnología original, especularemos sobre su evolución dentro de Microsoft y la relación existente entre su desarrollo, diversos proyectos y la tecnología finalmente presentada.

¿Será, entonces, este artículo una oda a los padres fundadores? Nada más lejos de nuestra intención pues, como se verá, nada nuevo propusieron puesto que alguna de las ideas verdaderamente originales, según nuestro propio y miope punto de vista, ya ha cumplido las cuatro décadas.

1. Introducción

En febrero de 1994 un grupo de investigadores de la Universidad de California en Berkeley funda Colusa Software, apareciendo un año después las primeras versiones beta de su principal producto, Omniware, un sistema que permitía la generación y ejecución de código móvil en Internet.

1 La versión final de este trabajo se terminó el 3 de mayo de 2001.

Omniware proporcionaba, como el resto de métodos de código móvil, portabilidad y seguridad: los mismos módulos Omniware, una vez compilados, podían ejecutarse en múltiples plataformas; por otro lado, el acceso de dichos módulos a los recursos del host sobre el que se ejecutaban podía controlarse de forma precisa.

Además de las cualidades anteriores, Omniware tenía dos características que lo diferenciaban de los sistemas existentes hasta la fecha:

Por un lado, empleaba una técnica conocida como software fault isolation (aislamiento de fallos por software) o SFI que permitía la ejecución segura de lenguajes inseguros, como por ejemplo C y C++; esta característica posibilitaría, en principio, la utilización de cualquier lenguaje de programación tradicional para el desarrollo de código móvil.

Por otro, Omniware era más rápido que el resto de soluciones disponibles en la época (fundamentalmente Java) puesto que el código móvil compilado no era interpretado en el host destino sino completamente traducido a código máquina nativo y, posteriormente, ejecutado.

Estas cuatro características (portabilidad, seguridad, independencia del lenguaje de programación y rendimiento adecuado) situaban a Omniware en una posición inmejorable para convertirse en un sustrato universal para la programación en el web [LUC95].

2. Colusa Omniware

Omniware estaba constituida, principalmente, por dos componentes: un entorno integrado de desarrollo y un entorno de ejecución. El primero era, básicamente, un compilador de C/C++ que generaba módulos Omniware mientras que el segundo de los componentes proporcionaba los mecanismos necesarios para la carga de los módulos, su traducción a código máquina nativo y su posterior ejecución.

Estos dos elementos sustentaban las cuatro características fundamentales de Omniware:

• Portabilidad: el compilador traducía C/C++ a código para una máquina abstracta, OmniVM, independiente de la plataforma destino; este código intermedio era traducido a código nativo por el entorno de ejecución, que sí cambiaba entre plataforma y plataforma.

• Seguridad: el acceso de cada módulo Omniware a los recursos del host se controlaba de forma precisa mediante una serie de permisos de acceso. Además, gracias a la SFI, Omniware garantizaba la ejecución segura de módulos externos aún cuando p udieran presentar fallos.

• Independencia del lenguaje: Omniware no introducía un nuevo lenguaje de programación sino que se basaba en la utilización de C/C++. La máquina

Page 2: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

2

abstracta para la cual generaba código el compilador Omniware tampoco estaba diseñada centrándose en un lenguaje específico, al contrario que la JVM, con lo cual la incorporación de nuevos lenguajes era tan sencilla como el desarrollo de nuevos compiladores que generasen código Omniware.

• Rendimiento adecuado: los módulos Omniware se ejecutaban casi a la misma velocidad que código máquina nativo puesto que no eran interpretados sino traducidos a código nativo ejecutándose este último; los intérpretes de la época comparados con Omniware eran diez o cien veces más lentos [COL95].

En Fig. 1 se muestra la forma en que se interrelacionaban los diferentes elementos del sistema.

Servidor origen

Módulo Omniware

Módulo Omniware

Módulo Omniware

Entorno de Desarrollo (OmniC/C++)

Código fuente C/C++

Código fuente C/C++

Host destino

Entorno de ejecución

(OmniRun)

Red

Fig. 1 Componentes fundamentales de la plataforma Omniware.

2.1. Entorno integrado de desarrollo

El entorno integrado de desarro llo contaba con un compilador de C/C++ (ANSI C, K&R C y ANSI C++) a código Omniware así como con una herramienta de depuración. De los dos elementos constituyentes de la plataforma Omniware, sin duda, este entorno era el menos importante; [COL95] dedica tan sólo tres párrafos al mismo mientras que [LUC95] deja claro que Omniware no iba a estar atada a un único lenguaje:

[Omniware] puede soportar cualquier lenguaje de programación. El actual sistema Omniware proporciona un entorno de desarrollo para C y C++ denominado OmniC++; el desarrollo de un front-end para Visual Basic está en marcha. Nosotros [Colusa] empezamos con C y C++ por dos razones. Primera, el dar soporte a lenguajes consolidados elimina la necesidad de un proceso de aprendizaje para un nuevo lenguaje. Segunda, OmniC++ permite a los desarrolladores incluir código heredado en sus aplicaciones web.

Como se puede ver, lo fundamental en Omniware no eran ni las herramientas de desarrollo ni el lenguaje de programación sino el entorno de ejecución.

2.2. El entorno de ejecución

El entorno de ejecución de Omniware, OmniRun [COL95] u ORE (Omniware/OmniRun Runtime Environment) [LUC95], proporcionaba un conjunto de servicios del sistema para gestionar los módulos Omniware, la memoria y la sincronización de procesos. Además, el ORE proporcionaba una serie de llamadas protegidas que podían ser exportadas a módulos inseguros; tales llamadas validaban sus argumentos y garantizaban así la imposibilidad de que un módulo Omniware violase la integridad de las aplicaciones en el host.

Los servicios ofrecidos por el entorno de ejecución podían clasificarse en cuatro grupos:

• Servicios del núcleo: proporcionaban rutinas para la carga y descarga de módulos, también definían los recursos a los que cada módulo Omniware tenía acceso.

• Gestión de memoria: OmniRun utilizaba un gestor de memoria pool-based con dominios de protección; se trataba de un sistema flexible que permitía al host elegir entre una amplia variedad de políticas de protección de memoria.

• Servicios de sincronización: proporcionaba tanto variables condicionales como mutex locks. Al igual que el gestor de memoria también disponía de dominios de protección.

• Servicios de utilidad: conjunto de rutinas para facilitar la interacción entre el host y Omniware.

Para proporcionar tales servicios el ORE estaba constituido por dos elementos: la máquina abstracta Omniware, OmniVM (Omniware Virtual Machine) y el API Omni32.

Los módulos Omniware se ejecutaban sobre OmniVM de forma análoga a como se ejecutan los bytecodes de Java sobre la JVM (con la salvedad de que el código Omniware nunca era interpretado).

El API Omni32, por otro lado, proporcionaba bibliotecas independientes del sistema operativo para visualización gráfica, gestión de memoria, E/S, hilos y sincronización; dichas funciones podían ser utilizadas de forma segura desde código Omniware inseguro.

Así pues, la máquina abstracta OmniVM constituía el componente primordial del ORE y, por extensión, de la plataforma Omniware. A lo largo de los apartados siguientes se detallarán las características arquitectónicas de dicha máquina, el sistema de protección que implementaba, su juego de instrucciones y detalles sobre el rendimiento que alcanzaba.

2.3. La máquina abstracta OmniVM

Según [ALI96] OmniVM generaliza el concepto de máquina abstracta para dar lugar a una “arquitectura computacional definida por software” (software-defined computer architecture o SDCA):

Una arquitectura computacional definida por software [software-defined computer architecture] (SDCA) implementa un juego de instrucciones virtuales, un gestor de memoria virtual y un modelo virtual de excepciones. Nuestra SDCA, la máquina abstracta Omniware (OmniVM), proporciona un espacio de direcciones segmentado, hace cumplir los permisos

Page 3: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

3

especificados por el host para el acceso a ese espacio de direcciones y dispara a los módulos una excepción de violación de acceso cuando éstos intentan realizar un acceso no autorizado a un segmento de memoria.

2.3.1. Arquitectura

La SDCA OmniVM era una máquina de registros (RISC) que definía un juego de instrucciones, un juego de registros, un formato de datos, un modelo de excepciones y un modelo de memoria virtual segmentada. Este diseño perseguía tres objetivos:

• Portabilidad: OmniVM debía ser portable a un gran número de arquitecturas destino; además, debía ser suficientemente potente y flexible como para soportar la ejecución segura de cualquier lenguaje de programación.

• Eficiencia: el proceso de traducción llevado a cabo por OmniVM debía ser rápido y obtener código de alto rendimiento en todas las arquitecturas destino.

• Facilidad de generación de código: OmniVM debía ser una arquitectura destino sencilla para un compilador de un lenguaje de alto nivel.

Diseño del procesador OmniVM fue diseñada para poder mapearse de forma

sencilla y directa sobre la mayor cantidad posible de procesadores; cada instrucción de la máquina abstracta se correspondía con una o varias instrucciones de la máquina destino y cada registro con un registro del procesador objetivo o con una posición de memoria. Puesto que la mayoría de las instrucciones equivalían a una única instrucción en la máquina destino, la mayor parte del código generado podía ser optimizado por el compilador dejando al traductor la relativamente simple tarea de convertir directamente el código Omniware a código máquina nativo. De esta forma el sistema conseguía una gran eficiencia tanto en el proceso de traducción como en el rendimiento del código generado.

Como se mencionó anteriormente, OmniVM era una máquina de registros (procesador RISC) aunque enriquecido con características de alto nivel (CISC). ¿Por qué razón se optó por esa arquitectura y no por una máquina de pila con instrucciones complejas (como por ejemplo JVM)? Por dos razones:

Primera, el uso excesivo de instrucciones de alto nivel complicaría el proceso de compilación puesto que, para poder aprovechar dichas instrucciones, los compiladores deberían manejar un elevado número de casos especiales para la generación de código; por otro lado, también incrementaría la complejidad de los traductores OmniVM.

La segunda y más importante, un compilador no puede seleccionar las instrucciones de la máquina virtual en función del tiempo que requieren para su ejecución; en caso de haber un elevado número de instrucciones (arquitectura CISC pura) el compilador podría optar por generar el código más compacto posible lo cual no tendría por que ser necesariamente la mejor solución en todos los casos. Por esa razón, era preferible proporcionar un conjunto reducido de instrucciones que, idealmente, emplearían el mismo tiempo en su ejecución (arquitectura RISC) facilitando así la labor del compilador.

Según [LUC95] en el diseño del juego de instrucciones final se aplicaron las siguientes reglas:

• Si una instrucción de alto nivel podía construirse mediante instrucciones RISC se incluían las instrucciones RISC.

• Si una instrucción de alto nivel no podía construirse a partir de instrucciones más simples era incluida siempre y cuando dicha instrucción fuera empleada de forma significativa en aplicaciones representativas en máquinas CISC.

• Si una instrucción de alto nivel era necesaria para estandarizar el manejo de enteros y flotantes era incluida.

En 2.3.3 se describirá con más detalle el juego de instrucciones de la máquina virtual.

Juego de registros OmniVM tenía 16 registros enteros (de 32 bits) y 16

registros flotantes (de 64 bits); al traducirse sobre máquinas RISC reales, los registros OmniVM se mapeaban directamente sobre registros físicos, mientras que en el caso de procesadores CISC (por ejemplo los x86) algunos registros debían mapearse sobre memoria.

El número de registros óptimo para la máquina se determinó de manera empírica; según [ALI96] menos de 16 registros causaba un descenso del rendimiento en procesadores Sparc mientras que más de 16 no lo incrementaban de forma significativa.

Formato de los datos La SDCA OmniVM definía el tamaño de los tipos de datos

básicos y soportaba enteros de tipo byte, media palabra y palabra (8, 16 y 32 bits, respectivamente) así como flotantes de simple y doble precisión 2.

Por otra parte, los tipos de datos básicos eran endian-neutrales puesto que no se indicaba la distribución de los bytes dentro de medias palabras y palabras ni de las medias palabras dentro de las palabras. OmniVM proporcionaba instrucciones para asegurar la portabilidad entre máquinas con distinta ordenación de los bytes.

2.3.2. Esquema de protección

Como ya se dijo con anterioridad, una de las características fundamentales de la plataforma Omniware era la ejecución segura de código inseguro; esta protección debía garantizarse para cualquier módulo, independientemente del lenguaje de programación empleado en su generación. Por esa razón, el mecanismo de protección debía estar implementado en la propia máquina virtual.

Así, OmniVM proporc ionaba un espacio de direcciones segmentado de manera similar al existente en un ordenador convencional; gracias a esta segmentación se podían definir diferentes contextos en memoria, denominados dominios de protección. Cada dominio de protección tenía asociada una tabla en la que se especificaban los permisos de acceso a memoria (escritura y lectura).

Cada vez que un módulo Omniware era cargado se le asignaba un nuevo dominio de protección; por defecto, un hilo

2 En [ALI96] se señala la intención de soportar enteros de 64 bits y direccionamiento también de 64 bits en versiones posteriores a la versión beta 1.68; hasta donde sabemos dicha versión pudo muy bien ser la última pues en marzo de 1996 (dos meses antes de la fecha de la referencia) Colusa Software fue adquirida por Microsoft.

Page 4: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

4

de ejecución en un dominio sólo podía acceder a su pila así como al código y datos del módulo asociados al mismo dominio de protección. Sin embargo, era posible cargar múltiples módulos en un solo dominio de protección de tal forma que todos ellos podían compartir los mismos permisos de acceso a memo ria.

Para implementar este esquema de protección OmniVM empleaba una técnica conocida como software fault isolation (aislamiento de fallos por software) o SFI cuyas dos características esenciales son las siguientes:

1. Se basa en la semántica proporcionada por la arquitectura del procesador y nunca en un lenguaje fuente de alto nivel.

2. Emplea una forma de chequeo en tiempo de ejecución que puede ser optimizado de tal forma que la sobrecarga debida al mecanismo de protección sea reducida.

Los rasgos básicos de la técnica tal y como los utilizaría la máquina OmniVM fueron descritos en [WAH93]. En dicho artículo se presenta SFI como un mecanismo para poder extender aplicaciones (bases de datos, sistemas operativos, etc.) mediante módulos externos no fiables sin que el aislamiento de dichos módulos repercutiera de manera excesiva en el rendimiento general de la aplicación.

Para conseguir ese objetivo se propone implementar el aislamiento de fallos en un espacio de direcciones único; dicho espacio está dividido en segmentos separados lógicamente, denominados dominios de fallo3, de tal forma que el código y los datos de un módulo no fiable son cargados en uno de dichos segmentos.

Cada dominio de fallo, está constituido por un conjunto de direcciones de memoria contiguas (dentro del espacio único original) y tiene asociado un identificador único que es utilizado para controlar el acceso a los recursos.

Por sí sola, esta medida no garantiza seguridad alguna puesto que nada puede impedir que el código del módulo cargado en un dominio acceda (accidental o intencionadamente) a direcciones pertenecientes a otros dominios; por esa razón, el código máquina del módulo debe ser modificado para prevenir accesos fuera del dominio en que esté confinado. De esta forma los módulos están completamente aislados unos de otros puesto que no existe ninguna posibilidad de acceso a dominios de fallo ajenos, exceptuando, claro está, mediante el empleo de mecanismos proporcionados por el sistema de protección para permitir la comunicación segura entre dominios (mecanismo que será tratado más adelante en este mismo apartado).

Para la modificación del código [WAH93] describe dos técnicas: comparación de segmentos (segment matching) y confinamiento de direcciones (address sandboxing4); en ambas es necesario identificar las instrucciones inseguras 5 e introducir código de control antes de cada una de ellas.

3 Los dominios de protección de la terminología Omniware. 4 La traducción del término address sandboxing no sería la propuesta, sin embargo, el autor considera que la adaptación expresa mejor la esencia del concepto que una versión literal como “introducción de direcciones en una caja de arena”. 5 [WAH93] define instrucción insegura como “cualquier instrucción que salte o escriba sobre una dirección de memoria que no pueda ser estáticamente verificada como perteneciente al segmento correcto”.

En el caso del método de comparación de segmentos dicho código determina si la instrucción insegura accede a una dirección del propio dominio o de un dominio ajeno; si el chequeo falla el código insertado saltará a una rutina de error del sistema parando la ejecución del módulo. Esta técnica requiere el empleo de cuatro instrucciones en arquitecturas RISC típicas, ver Fig. 2.

reg-dedicado ßß dirección destino Mover dirección destino a un registro dedicado.

reg-normal ßß (reg-dedicado >> reg-shift) Desplazar hacia la derecha la dirección para obtener identificador de segmento. reg-normal no es un registro dedicado. reg-shift es un registro dedicado.

comparar reg-normal y reg-segmento reg-segmento es un registro dedicado.

atrapar si no iguales Si la dirección donde se va a escribir está fuera del segmento se lanza una “excepción”.

instrucción almacenamiento usa reg-dedicado La instrucción insegura.

Fig. 2 Pseudocódigo para la técnica de comparación de segmentos.

La técnica de confinamiento de direcciones es más sencilla; el código insertado simplemente establece los bits más altos de la dirección destino de tal forma que ésta siempre pertenecerá al dominio de fallo del módulo. Como se puede apreciar, esta técnica no detecta direcciones ilegales, tan sólo garantiza que las instrucciones inseguras no pueden afectar a ningún dominio más que el suyo. La técnica de confinamiento de direcciones precisa tan solo de dos instrucciones aritméticas, ver Fig. 3.

reg-dedicado ßß dirección destino & reg-mascara Utiliza el registro dedicado reg-mascara para poner a 0 los bits correspondientes al identificador de segmento.

reg-dedicado ßß reg-dedicado | reg-segmento Utiliza el registro dedicado reg-segmento para establecer los bits del identificador de segmento.

instrucción almacenamiento usa reg-dedicado La instrucción insegura.

Fig. 3 Pseudocódigo para la técnica de confinamiento de direcciones.

Mediante el empleo de un espacio de memoria único segmentado y la certificación 6 de la legalidad de las direcciones se consigue ejecutar código inseguro de forma segura por lo que se re fiere a las posibles interferencias entre módulos; sin embargo, si los módulos realizaran llamadas al sistema podrían corromper recursos necesarios para la aplicación principal.

Por esa razón, SFI implementa un mecanismo extra para la realización de dichas llamadas desde módulos inseguros; cada módulo, además de ejecutarse en un dominio aislado, sólo puede acceder a los recursos del sistema mediante la utilización de llamadas seguras entre dominios de fallo7. Se reserva un dominio para la colocación de código de arbitraje que determina si una llamada al sistema realizada desde otro dominio de fallo es o no segura. Así, al cargar un módulo

6 Ya se ha indicado que Omniware empleaba la técnica de SFI, sin embargo, por lo que respecta a la validación de las direcciones destino no empleaba ni la técnica de comparación de segmentos ni la de confinamiento de direcciones sino un mecanismo derivado de los dos anteriores [WAH95]; por esa razón, el autor denominará, a partir de ahora, al código previo a las instrucciones inseguras código de certificación. 7 cross-fault-domain RPC (Remote Procedure Call).

Page 5: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

5

además de “certificar” las instrucciones inseguras se convierten todas las llamadas al sistema en llamadas seguras entre dominios.

Como se puede apreciar, los tres mecanismos descritos (dominios de fallo, certificación de direcciones y llamadas seguras) garantizan la ejecución segura de código no fiable; dicha ejecución además de segura es eficiente incurriendo en un aumento del tiempo de ejecución de la aplicación de alrededor del 4% [WAH93]. Sin embargo, esta sobrecarga es debida tan sólo al mecanismo de protección, en el apartado 2.4 se realiza un análisis más detallado del rendimiento de la plataforma Omniware completa8, así como una comparativa con otros sistemas disponibles en la época.

2.3.3. Juego de instrucciones

Un aspecto extraordinariamente interesante a la hora de tratar una máquina abstracta es el juego de instrucciones de la misma. En este punto se tratará de exponer las principales características del “presumible” juego de instrucciones de OmniVM, dicha calificación es necesaria puesto que, hasta donde sabe el autor, ningún artículo ni documento técnico9 de Colusa Software proporcionó tal repertorio de instrucciones; sin embargo, en [WAH95] figura un juego parcial que sólo podría pertenecer a la máquina OmniVM 10 y es ése sobre el que se basa el presente apartado.

Las instrucciones de la máquina OmniVM podían ser de 32, 64 o 96 bits de longitud; comenzando siempre por un código de operación, opcode, de 5 bits seguido de, al menos, un operando.

Existían nueve formatos11 posibles para la codificación de las instrucciones y todos menos dos incluían junto con el opcode un subcódigo de operación, subopcode, también de 5 bits. Mediante esta combinación de formatos, códigos y subcódigos el repertorio de instrucciones alcanzaba las 16912 instrucciones que, según [WAH95], constituían una “lista no exhaustiva de instrucciones para la máquina abstracta”. Efectivamente, manteniendo la compatibilidad con dicho juego podría alcanzarse el número de 1481 instrucciones; sin embargo, teniendo en cuenta que tal cantidad sextuplica el juego de instrucciones de la JVM y se aleja considerablemente de la filosofía de los procesadores RISC es más lógico pensar

8 En el rendimiento final de la plataforma intervendrán, además de la sobrecarga debida al mecanismo de protección, el tiempo necesario para la traducción del código intermedio a código máquina nativo y la eficiencia del código intermedio generado por los compiladores. 9 En [WAH97] se referencian dos documentos que, tal vez, incluian dicho juego de instrucciones:

• Getting Started With Omniware. Colusa Software, Inc., 1995.

• Omniware Virtual Machine Architecture Reference Manual, Versión 1.0 Beta . Microsoft Corporation.

Lamentablemente, ha resultado imposible localizar ninguno de ellos. 10 Dicho documento es una patente sobre métodos para la implementación de máquinas virtuales; los inventores son R. Wahbe y S. Lucco y fue presentada en diciembre de 1995 (coincidiendo con la publicación de [COL95] y [LUC95]); desde abril de 1996 dicha patente pertenece a Microsoft. 11 REG32, IMM32, JUMP32 (de 32 bits de longitud), EXT64, IMM64, REG64, BF64 (de 64 bits de longitud), REG96 y DOUBLE96 (de 96 bits de longitud); el lector interesado encontrará información más detallada sobre el juego de instrucciones de OmniVM en el Apéndice. 12 Ver el punto “Comentarios al juego de instrucciones” del apéndice.

que el número real de instrucciones de OmniVM estaría próximo a esas 169 y, muy probablemente, por debajo de las casi 256 de la JVM.

Estas instrucciones podían clasificarse en los siguientes grupos:

• Instrucciones aritméticas: suma, resta, menos unario, multiplicación, división, módulo, valor absoluto y raíz cuadrada.

• Instrucciones lógicas: and, or inclusivo, or exclusivo y not.

• Instrucciones de desplazamiento: desplazamiento a la izquierda y desplazamiento a la derecha.

• Instrucciones de control: llamada a subrutina, llamada a subrutina lejana, salto incondicional, salto incondicional lejano, volcar/recargar registro a/desde memoria y retorno de subrutina.

• Instrucciones de bifurcación: bifurcar si iguales, bifurcar si distintos, bifurcar si menor o igual, bifurcar si menor, bifurcar si mayor o igual y bifurcar si mayor.

• Instrucciones de carga y almacenamiento: cargar y almacenar.

• Otras instrucciones: cargar valor constante, insertar/extraer bits, mover, mover y bifurcar, mover desde memoria extendida, redondear a entero/flotante/doble, convertir doble a entero/flotante, convertir flotante a doble/entero y convertir entero a flotante/doble.

Por lo que respecta a modos de direccionamiento, OmniVM admitía los siguientes:

• inmediato, • directo, • desplazamiento de registro, • registro indexado, • símbolo más desplazamiento, • símbolo más registro y • símbolo más desplazamiento más registro. La mayor parte de las características anteriores se

corresponden con una arquitectura RISC típica aunque, como ya se dijo, OmniVM presentaba algunas características de alto nivel presentes en procesadores CISC; así, se introducen una serie de instrucciones para movimiento de bloques de datos (bfins.* y bfext.*) y se enriquecen los modos de direccionamiento disponibles respecto a los procesadores RISC.

Es precisamente esta última característica la principal diferenciación de la máquina abstracta respecto del paradigma RISC, el objetivo que se perseguía era conseguir que la práctica totalidad de instrucciones con acceso a memoria pudieran expresar la dirección efectiva en una sola instrucción; de esta manera los traductores sobre máquinas CISC podrían utilizarlas de forma directa y los traductores sobre máquinas RISC podrían generar código óptimo.

El lector interesado puede consultar en el apéndice el juego de instrucciones de la máquina OmniVM.

2.4. Integración de todos los componentes

Hasta el momento se han descrito los distintos componentes, subcomponentes y elementos de la plataforma Omniware:

Page 6: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

6

• Entorno integrado de desarrollo: o OmniC/C++ o OmniDebug o Otras herramientas...

• Entorno de ejecución (OmniRun/ORE): o Omni32 API:

§ Visualización gráfica. § Gestión de memoria. § Entrada/Salida. § Hilos. § Sincronización.

o Omniware Virtual Machine (OmniVM):

§ Arquitectura del procesador. § Esquema de protección (SFI). § Juego de instrucciones (RISC/CISC)

Así como sus principales características:

• Portabilidad. • Seguridad. • Independencia del lenguaje de programación. • Rendimiento adecuado:

o Arquitectura destino sencilla para los compiladores.

o Eficiencia en el proceso de traducción de Omniware a código máquina nativo.

Conviene ahora proporcionar una visión general de la plataforma mostrando la forma en que los distintos componentes permitían la generación de código móvil seguro y eficiente independientemente del lenguaje de programación elegido; para ello se expondrá el proceso que transcurre desde un programa en C/C++ a un módulo ejecutándose en un host destino cualquiera:

1. El código fuente en C/C++ es traducido mediante OmniC++ a código Omniware binario; debido a las especiales características de la máquina abstracta OmniVM, este compilador puede llevar a cabo toda una serie de optimizaciones sobre el código generado.

2. El módulo Omniware es descargado desde el servidor al host destino por el entorno d e ejecución.

3. El traductor del entorno de ejecución genera a partir del código Omniware código máquina nativo para el host destino; este proceso es rápido y eficiente puesto que la inmensa mayoría de instrucciones del juego de OmniVM se corresponden de forma directa con instrucciones nativas de arquitecturas RISC y CISC.

4. Las instrucciones inseguras del código nativo obtenido son “certificadas” mediante SFI.

5. Se optimiza el código anterior para obtener uno equivalente pero con un número mínimo de instrucciones certificadas.

6. Se optimiza el nuevo código de forma local y global para incrementar su rendimiento.

7. El código máquina final es ejecutado sobre el host destino:

a. Si el código no viola el modelo de protección de la máquina abstracta ejecuta de forma normal.

b. Si el código viola el modelo de protección es detenido y el control pasa a un manejador de excepciones.

La Fig. 4 trata de expresar de forma esquemática la esencia de Omniware: los módulos Omniware trabajan con el juego de

registros y el espacio de direcciones de la máquina abstracta, sin embargo, los accesos a memoria deben satisfacer los requisitos de seguridad impuestos por el modelo de protección, representados por un conjunto de permisos; si los permisos son satisfechos la eje cución transcurre con normalidad y se interrumpe si son quebrantados.

Para terminar con los aspectos generales de Omniware y antes de realizar un breve análisis de su rendimiento es necesario señalar un par de aspectos poco claros de la plataforma, ambos relacionados con el mecanismo de protección.

El primero de ellos hace referencia a la técnica empleada para “certificar” las instrucciones inseguras, ¿cuál se aplica? ¿comparación de segmentos o confinamiento de direcciones? [COL95] y [LUC95] tan sólo mencionan el concepto de dominios de protección y la utilización de SFI sin entrar en mayores detalles mientras que [WAH95] proporciona un ejemplo de código que aparenta ser un híbrido de ambas técnicas.

r1:

Juego de

registros

.

.

.

r2:

Programa para la

máquina

abstracta Memoria

de la máquina

abstracta

.

.

.

Permisos de acceso

a memoria

N

Fig. 4 Módulo Omniware ejecutándose sobre la máquina abstracta.

Por otra parte, en ese mismo documento se describe la forma en que tras aplicar la técnica a una instrucción ilegal la dirección resultante es inválida (resultado de aplicar confinamiento de direcciones) mientras que en otro punto podría entenderse que las instrucciones son comprobadas y un código de excepción es ejecutado al producirse un acceso ilegal (comparación de segmentos). No obstante, también se afirma lo siguiente:

Los saltos condicionales son bastante lentos en la mayor parte de los procesadores, así que usar -1 [establecer una dirección inválida] y permitir que el módulo realice un acceso a memoria es generalmente mucho más eficiente que verificar la legalidad de la instrucción y realizar un salto [a código de excepción].

Page 7: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

7

Con lo cual puede afirmarse que el mecanismo empleado por Omniware es SFI con una variedad de confinamiento de direcciones (address sandboxing).

El segundo punto confuso se refiere al momento en que se aplica de forma efectiva la SFI al código para confinar las sentencias inseguras. [WAH93] al presentar las características originales de la técnica parece indicar que será el compilador el encargado de introducir el código de confinamiento; sin embargo el método expuesto en [WAH95] deja claro que el aislamiento de fallos es posterior a la traducción del código Omniware a código máquina nativo. [ALI96] viene a ratificarlo:

El actual sistema Omniware introduce estos chequeos cuando un módulo es cargado en el espacio de direcciones del host y es traducido de OmniVM a código nativo.

La principal ventaja que aporta esta opción es la de simplificar la implementación de compiladores para la máquina abstracta. El compilador tan sólo genera código y no debe preocuparse de aspectos de seguridad; como se puede ver, un módulo no deja de ser inseguro hasta el mismo momento es que es cargado y traducido a código nativo.

Esta estrategia es muy diferente de la empleada, por ejemplo, por Java y la JVM; en este caso la protección viene garantizada por un lenguaje estric to y fuertemente tipado13, obligando a los compiladores a ser “conscientes” del sistema de protección.

2.5. Rendimiento

Los desarrolladores de Omniware afirmaban que su sistema era independiente del lenguaje, seguro, portable y rápido. Esperamos haber clarificado de forma suficiente los tres primeros puntos, ahora trataremos de proporcionar un cierto sustento a la aseveración de que Omniware era más rápido que el resto de sistemas existentes en la época.

Dejando a un lado la independencia del lenguaje y las características internas de la máquina abstracta implementada existían pocas diferencias entre lo que ofrecían Omniware y Java al web14, la única posibilidad que tenía Omniware de medrar era superando en algún aspecto a la JVM y, en aquella época, el talón de Aquiles de Java era la velocidad de ejecución.

En el siguiente apartado se presentarán algunos de los resultados ofrecidos por los desarrolladores de Omniware sobre el rendimiento del sistema en comparación con código nativo. Tras estos se ofrecen las conclu siones de un estudio “independiente”15 que tenía como finalidad comparar una serie de tecnologías para la extensión de sistemas operativos; dos de estas tecnologías fueron Java y Omniware.

13 Junto con medidas de protección impuestas en el host destino, la famosa “caja de arena”; la opción elegida para Omniware es más simple: toda la protección corre a cargo del host. 14 Sun presentó HotJava en mayo de 1995, Colusa lanzó Omniware en agosto de ese mismo año. Cabe decir que Omniware no pretendía ofrecer soluciones únicamente para el web, [COL95] proponía el uso de la plataforma para proporcionar mecanismos de personalización y extensión de aplicaciones. 15 El estudio referido, [SMA96], fue apoyado por Sun Microsystems Laboratories.

2.5.1. Comparación entre Omniware y código nativo

Existen cuatro referencias que ofrecen datos cuantitativos sobre el rendimiento de Omniware en relación con código máquina nativo: [COL95], [LUC95], [WAH95] y [ALI96]; los tres primeros utilizan una versión Omniware indeterminada del año 1995 (a partir de ahora, Omniware’95) mientras que la última emplea la versión beta 1.68 de 1996 (Omniware’96).

Para evaluar el rendimiento del código Omniware se escogieron un grupo de programas de prueba con código fuente C disponible y se generaron los módulos binarios; mediante el compilador es tándar de la plataforma (cc o gcc) los binarios en código nativo y mediante OmniC los correspondientes módulos Omniware.

Los programas de prueba fueron cuatro benchmarks del SPEC92 (alvin, compress, ear, eqntott, gcc y xlisp) así como un grupo de aplicacio nes ampliamente utilizadas: lcc, li, tcl y tex. En el caso de los benchmarks se emplearon como datos de entrada los proporcionados en el propio SPEC92; para Tcl se utilizaron el conjunto de pruebas que lo acompañan en la distribución; para Tex se utilizó un documento de cuatro páginas con ecuaciones complejas y para lcc el fichero lcc x86.c.

Los estudios realizados para Omniware’95 se llevaron a cabo sobre dos plataformas hardware:

• SPARC: SPARCstation 5, 96MB, Solaris 2.4. • x86: equipo indeterminado.

Para Omniware’96 se hicieron sobre cuatro plataformas:

• SPARC: SPARCstation 10, Solaris 2.4. • x86: Pentium 90MHz, Windows NT 3.5. • PowerPC: IBM RS/6000, PowerPC 601, AIX v.3. • Mips: SGI Indigo-2, Mips R4400, Irix 5.2.

Los resultados obtenidos en ambos casos se muestran en la Tabla 1 y en la Tabla 2, se trata del porcentaje de sobrecarga sobre el tiempo de ejecución introducido al ejecutarse un módulo Omniware en lugar del código nativo equivalente.

Sobrecarga (%) Programa SPARC x86

alvinn 9,77 30,00 compress 5,00 3,00 ear 5,23 ? eqntott 4,00 8,00 gcc 23,86 ? lcc 18,18 ? li 6,00 14,00 tcl 5,15 ? tex 4,88 ? xlisp 5,07 ? Sobrecarga media (%) SPARC x86 8,71 13,75

Tabla 1 Evaluación del rendimiento de Omniware’95 sobre las plataformas SPARC y x8616.

16 Esta tabla muestra de forma conjunta los datos presentados en [COL95], [LUC95] y [WAH95], sólo en el tercero se presentan resultados para la plataforma Intel y no se emplean todos los programas de prueba utilizados en las otras referencias, de ahí la ausencia de algunos valores.

Page 8: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

8

Sobrecarga (%)

Programa SPARC x86 PPC Mips alvinn 7,00 25,00 8,00 20,00 compress 2,00 2,00 23,00 4,00 eqntott 4,00 6,00 35,00 20,00 li 5,00 11,00 18,00 10,00 Sobrecarga media (%) SPARC x86 PPC Mips 4,50 11,00 21,00 13,50

Tabla 2 Evaluación del rendimiento de Omniware’96 sobre las plataformas SPARC, x86, PowerPC y Mips.

Como se puede apreciar en las tablas anteriores Omniware podía ejecutar de forma segura programas escritos en C a una velocidad muy razonable (la sobrecarga media máxima era del 21%) en comparación con los tiempos de ejecución del equivalente código inseguro. Según sus desarrolladores estos resultados podían ser mejorados pero, aún así, era el sistema de código móvil más rápido de su época y el primero en proporcionar código seguro de forma independiente del lenguaje.

2.5.2. Comparación entre Omniware y Java

Obviamente, los resultados ofrecidos por los desarrolladores de Omniware mostrarían los aspectos más favorables de su propio producto; además, aún cuando en ningún momento comparan de forma directa Omniware con Java, discretamente se da a entender que JVM es mucho más lenta que OmniVM, así según [LUC95]:

Las implementaciones actuales de la máquina abstracta de Java emplean un intérprete de bytecode. Aunque no se han publicado estudios completos de rendimiento las actuales estimaciones indican que los programas en Java se ejecutan entre un 1200% y un 1300% más lentos que código nativo.

Sin embargo, ¿era realmente Omniware tan rápido como afirmaba Colusa y Java tan lento como insinuaba? ¿No hubo ningún “observador independiente” que examinase ambas tecnologías?

La respuesta a esta última cuestión es afirmativa, Small y Seltzer [SMA96] llevaron a cabo un estudio sobre las diferentes opciones tecnológicas disponibles para el desarrollo de sistemas operativos extensibles. Seleccionaron un grupo de tecnologías –C, Java17, Modula -3, Omniware18 y Tcl– así como una serie de plataformas –Alpha, HP-UX, Linux y Solaris – para la realización de las pruebas; éstas consistieron en la modelización de diversos servicios que podrían proporcionarse como plug-in’s19 para un sistema operativo extensible:

• Un mecanismo de reemplazo de páginas en memoria virtual,

• el algoritmo de firma digital MD5 y • un disco lógico con un mecanismo de log intermedio

entre el sistema de ficheros y el disco físico.

17 Java versión alpha 3. 18 Omniware versión 1.0b r1.5 (probablemente muy similar a la beta 1.68 del apartado 2.5.1). 19 Se emplea aquí el término plug-in por resultar más familiar en la actualidad, sin embargo, [SMA96] utiliza el de graft (injerto).

Estos modelos se implementaron en todas las plataformas utilizando cada una de las tecnologías seleccionadas y se determinó la sobrecarga que introducían sobre el tiempo de ejecución al compararlo con el de código C. Por lo que respecta al presente artículo sólo son de interés los resultados obtenidos por Java y Omniware que se muestran en la Tabla 3.

Como se puede apreciar en la misma, Omniware introducía una sobrecarga media del 35,36% superior al 21% máximo que anunciaban sus desarrolladores aunque dentro del mismo orden de magnitud20; Java, en cambio, resultó ser casi unas 40 veces más lento que el equivalente código C y unas 27 veces más que el correspondiente código Omniware.

Por tanto, este estudio parece apoyar las afirmaciones de los desarrolladores de Omniware sobre el superior rendimiento de este sistema; sin embargo, [SMA96] en sus conclusiones hace notar un par de aspectos interesantes.

Los dos candidatos más convincentes son Java compilado y SFI [Omniware] con protección completa (lectura, escritura y salto). Ninguna está disponible en la actualidad [1996] pero ambas están bajo desarrollo. A corto plazo se podrá medir su rendimiento y compararlas directamente.

Tiempo de ejecución y sobrecarga

Prueba C Java Omniware Reemplazo de páginas 4,5µs

0% 141µs

3.033,33% 6,3µs

40,00% Algoritmo MD5 146ms

0% 10.368ms

7.001,37% 219ms

50,00% Disco lógico 1,90s

0% 24,60s

1.194,74% 2,20s

15,79% Sobrecarga media C Java Omniware 0% 3.743,15% 35,26%

Tabla 3 Comparación entre los tiempos de ejecución de Java y Omniware con código nativo inseguro21.

Los autores del estudio dejan claro que aunque Omniware da mejores prestaciones no ofrece aún un mecanismo completo de seguridad, dando a entender que en el momento en que se ofreciera protección total el rendimiento podría disminuir de forma palpable y aproximarse así al de una versión compilada de Java.

2.5.3. Conclusiones sobre el rendimiento de Omniware

La conclusión fundamental que puede extraerse de los estudios anteriores es que Omniware introducía una sobrecarga razonable respecto a la ejecución de código nativo y que, efectivamente, el rendimiento era muy superior al de la máquina de Java; no obstante, también resulta patente que se estaban evaluando las versiones beta de ambos productos y que la tendencia natural sería la de aumentar la velocidad de ejecución de Java (como t erminó sucediendo) y de disminuir la velocidad de ejecución de Omniware al introducir mecanismos de seguridad más complejos.

Sería realmente interesante poder comparar la actual JVM con una versión equivalente de OmniVM; sin embargo, tal cosa nunca sucederá puesto que, como ya se ha dicho en diversas

20 La discrepancia podría ser debida, al menos en parte, a la utilización de versiones diferentes de Omniware. 21 Estos datos se corresponden con pruebas efectuadas sobre Solaris/SPARC pues era la única de las plataformas seleccionadas para el estudio para la que existía una versión de Omniware.

Page 9: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

9

notas al pie, Microsoft adquirió Colusa Software en marzo de 1996, dos años después de su fundación y ¡siete meses! tras la presentación de Omniware [MSF96]:

REDMOND, Wash. – 12 Marzo, 1996 – Microsoft Corp. ha anunciado hoy la adquisición de Colusa Software Inc. [...] El producto Colusa Omniware permite a los desarrolladores crear componentes Internet de alto rendimiento y con protección de memoria mediante lenguajes de programación populares, incluyendo C y C++. [...] Microsoft incorporará las tecnologías de Colusa en futuras versiones de sus productos para Internet y herramientas de desarrollo [...]

Puede parecer que Omniware representaba tan sólo una mínima ventaja frente a una incipiente máquina virtual de Java; sin embargo, es muy posible que Microsoft considerase que no hay ventaja pequeña.

A partir de este instante el artículo cambia de tono para adquirir un cariz más especulativo al intentar determinar la probable evolución de la tecnología Omniware dentro de Microsoft así como las posibles ramificaciones e implicaciones de la misma en diversos productos de dicha compañía.

El próximo punto tratará aspectos relacionados directa o indirectamente con el desarrollo de Omniware dentro de Microsoft desde marzo de 1996 hasta 1997; el siguiente tratará sobre diversos proyectos presuntamente relacionados con Omniware así como aquellas tecnologías de Microsoft que han podido inspirarse en mayor o menor medida en la experiencia proporcionada por Omniware.

3. Microsoft Omniware

Este apartado se basa en [WAH97] y [ERN97]; la primera es una patente de Microsoft vinculada a [WAH95] –de hecho una ampliación de la misma– mientras el segundo es un artículo que sienta las bases de la técnica de compresión de código empleada por dicha patente.

A partir de tales documentos pueden extraerse las características fundamentales de Microsoft Omniware que la distinguen de la antigua Colusa Omniware:

• Una arquitectura totalmente renovada, • un sistema de protección completo, • la aplicación de SFI también en tiempo de

compilación y • la compresión de código.

Cada una de ellas será tratada con mayor detalle a continuación.

3.1. Arquitectura renovada

Aparentemente, Microsoft Omniware pretendió aproximar la arquitectura de la máquina virtual a la estructura típica de un procesador hardware; así, [WAH97] menciona buses de entrada y salida, registros, decodificador de instrucciones, ALU, etc. aunque no entra en detalles sobre dichos componentes. También se describe el juego de registros y los modos de direccionamiento que coincidían con los descritos para Colusa OmniVM.

Por lo que respecta al juego de instrucciones no se menciona de forma detallada, tan sólo se describen los distintos tipos existentes y se menciona la facilidad de traducción a

código nativo de arquitecturas CISC y RISC (en ese orden). Con los datos proporcionados apenas se puede decir nada acerca del nuevo juego aunque el código que se muestra como ejemplo del método de compresión y la descripción general del mismo nos lleva a pensar que lo s cambios efectuados respecto al de [WAH95] fueron mínimos y, en todo caso, el anterior juego de instrucciones de Colusa OmniVM sería un subconjunto del actual22.

Unidad Aritmético Lógica

Multiplexor

Registros / Decodificador

N

S.O Máquina virtual

Programalocal (A)

Programaexterno (B)

MAP

MAP

Libre

Memoriaprincipal/caché

Datosde

entrada

Datosde

salida

Fig. 5 Arquitectura mejorada de la máquina abstracta OmniVM.

Un cambio llamativo respecto a la versión anterior de OmniVM es la mención explícita de un programa de control de la máquina abstracta o “sistema operativo”; con toda probabilidad dicho sistema operativo implementaba la funcionalidad que Omniware encapsulaba en el API Omni32, el mecanismo de llamadas seguras entre dominios de fallo así como las distintas tablas empleadas por los programas en ejecución dentro de la máquina abstracta:

• La tabla de permisos de acceso a memoria (MAP) que indicaba los distintos permisos que tenía asignados un módulo para acceder a cada posición del espacio de direcciones.

• La tabla de símbolos que contenía una lista indexada de símbolos resueltos para su referencia directa desde el programa.

• La tabla de sitios que contenía una lista de sit ios externos que podían ser accedidos de forma segura para utilizar funciones no definidas en el programa en ejecución.

Sin embargo, y a pesar del nombre, dicho programa no era un auténtico sistema operativo; de hecho, la máquina descrita

22 Por esa razón en el Apéndice sólo se proporciona un juego de instrucciones genérico para OmniVM.

Page 10: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

10

carecía de conceptos como “anillos de protección” o “modo protegido”; tampoco existían instrucciones privilegiadas puesto que las operaciones sensibles no eran realizadas por la máquina sino por una aplicación fiable del host como por ejemplo un navegador web. Por ello, todos los programas que se ejecutaban sobre OmniVM lo hacían en “modo usuario”, es decir, de forma segura pero imposibilitando la construcción de sistemas operativos sobre la máquina virtual (lo cual nunca fue un objetivo perseguido por ésta ni otras máquinas abstractas, como la JVM).

3.2. Sistema de protección completo

Como se dijo en el apartado 2.5.3, Colusa Omniware no disponía de un sistema de protección completo, tan sólo evitaba la escritura no permitida en memoria mientras que la lectura de las mismas direcciones era factible; tales carencias fueron solventadas en la versión de Omniware de Microsoft.

Dicha versión permitía especificar para cada módulo unos permisos de acceso a memoria (memory access permission o MAP, ver Fig. 5) que podían ser los tres clásicos: lectura, escritura y ejecución. Los dos primeros indicaban, simplemente, si el programa podía escribir o leer sobre una dirección de memoria en particular; mientras que el permiso de ejecución servía para indicar si el contenido accedido, en teoría una instrucción, podía o no ser ejecutado.

Según las referencias consultadas las tablas de permisos indicarían para cada dirección de memoria virtual de la plataforma hardware subyacente la secuencia de permisos RWX permitidos para un módulo; sin embargo, creemos que es más factible que, por defecto, los módulos carecieran de permiso alguno sobre el espacio de direccionamiento completo y la tabla tan sólo indicase los permisos otorgados sobre segmentos de memoria contiguos.

3.3. Aplicación de SFI

Microsoft Omniware seguía empleando software fault isolation para garantizar la ejecución segura de código inseguro; como recordará el lector, dicha técnica consistía en examinar el código original, localizar las instrucciones potencialmente peligrosas (típicamente instrucciones de acceso a memoria y saltos) e introducir código que encapsule dichas instrucciones de tal forma que sólo puedan acceder a direcciones legales.

SFI permitía la utilización de dos métodos distintos para la encapsulación de instrucciones inseguras: comparación de segmentos y confinamiento de direcciones; mientras que Colusa Omniware empleaba una técnica híbrida basada en el confinamiento de direcciones la versión de Microsoft optó por el algoritmo de comparación de segmentos atrapando código del propio sistema los accesos ilegales.

Sin embargo, la diferencia fundamental en cuanto a la aplicación de SFI es que mientras que en la versión de Colusa es el traductor de OmniVM el encargado de introducir el código de certificación en la versión de Microsoft dicho proceso puede ser llevado a cabo en dos momentos: en el momento de compilación o bien durante la carga del módulo. De esta forma el rendimiento en el cliente es superior si el módulo Omniware ya ha sido “certificado”; sin embargo, aumenta la complejidad del traductor y de los compiladores: el primero debe determinar la seguridad o inseguridad de un módulo y en el segundo caso aplicar SFI, por otro lado, el compilador no se limita a ser un mero traductor de código fuente a código OmniVM sino que debe chequear los puntos

conflictivos y aplicar la misma técnica que empleará la máquina abstracta.

3.4. Compresión de código

Uno de los problemas más evidentes a los que se enfrenta un sistema de código móvil es el de la transmisión de los programas a través de una red que constituye un importante cuello de botella. Para aliviar dicho problema puede resultar interesante transmitir los programas en un formato comprimido para, a su llegada, descomprimirlos y proceder a su ejecución; a este código comprimido para transmisión se le denomina código wire (cable).

Los desarrolladores de Microsoft Omniware consideraron además otro cuello de botella, la memoria 23; en este caso el programa no sólo debe estar comprimido sino que dicho formato tiene que poder ser directamente ejecutado.

Así, si el sistema presentase ambos cuellos de botella los programas se transmitirían en formato wire para ser descomprimidos en el host destino a un formato comprimido interpretable. [ERN97] describe ambos métodos de compresión, denominando BRISC al código comprimido interpretable.

El método BRISC o Byte-coded RISC implementado en Omniware ofrecía las siguientes características:

• Un programa BRISC tenía aproximadamente el mismo tamaño que el código máquina x86 equivalente comprimido con gzip.

• El código BRISC podía ser interpretado a una velocidad 12 veces menor que el código descomprimido pero reduciendo el entorno de trabajo más de un 40%.

• El código BRISC podía ser traducido a código nativo a una velocidad de 2,5MB/s 24, 100 veces más rápido que cualquier compilador JIT de la época.

Así pues, Microsoft OmniVM podía interpretar BRISC directamente o traducirlo a código nativo y ejecutarlo posteriormente; según sus desarrolladores el tiempo de transmisión desde la red o el disco puede enmascarar parcial o totalmente el tiempo de recompilación, ejecutándose el código a 1,08 veces la velocidad de código máquina totalmente optimizado generado por Visual C++ 5.0 [ERN97].

Esa misma referencia proporciona los resultados obtenidos por OmniVM para diferentes programas; dichos datos se muestran en la Tabla 4.

Todas las columnas de la tabla, excepto JIT, muestran la relación existente entre tamaño (BRISC y gzip) o velocidad de ejecución (Ejecución e Interpretación) del código BRISC de un programa y su código máquina equivalente generado por Visual C++ 5.0; Ejecución incluye el tiempo de traducción de BRISC a código nativo; JIT indica la velocidad de traducción en MB/s en un Pentium 120MHz con 32MB de RAM.

Como se puede ver, los resultados obtenidos mediante BRISC son magníficos para un sistema de código móvil, los programas tienen un tamaño similar al de código nativo comprimido y pueden ser traducidos y ejecutados logrando una velocidad muy similar.

23 Tal vez Microsoft estaba planteándose utilizar Omniware en sistemas empotrados o PDAs. 24 En un Pentium 120MHz.

Page 11: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

11

Programa BRISC gzip Ejecución JIT Interpretación lcc 3.0 0,54 0,55 1,12 2,7 12,4 gcc 2.6.3 0,57 0,59 1,09 2,6 9,6 Word97 0,69 0,59 1,03 2,8 15,4 agrep 0,60 0,57 1,11 2,3 14,2 xlisp 0,59 0,57 1,05 2,4 12,3 expresso 0,53 0,55 1,08 2,4 11,6

Media BRISC gzip Ejecución JIT Interpretación

0,59 0,57 1,08 2,5 12,6

Tabla 4 Resultados obtenidos por OmniVM al emplear la técnica de compresión de código BRISC.

3.5. Conclusiones sobre MS -Omniware

Como se ha podido apreciar, Omniware, aunque fiel a su esencia, logró con Microsoft un mejor “acabado”: la arquitectura se hizo más compleja, se implantó de forma real un sistema completo de protección y se tomaron medidas para mejorar el rendimiento (aplicación de SFI en tiempo de compilación y compresión de código).

Probablemente, Colusa hubiera alcanzado parecidos resultados aunque quizás no tan rápidamente25; sin embargo, una cosa está clara y es que Omniware había dejado de ser una promesa para convertirse en un producto tangible que podía competir en un plano de igualdad con Java.

De hecho, no fueron pocos los que hablaron de un Java-killer y vincularon con el mismo esta tecnología; por eso, en el apartado siguiente, se hará un recorrido cronológico por los distintos pro yectos presuntamente relacionados con Omniware y mediante alguno de los cuales se suponía que Microsoft iba a socavar la hegemonía de la plataforma Java.

4. Los frutos de Omniware

Durante aproximadamente dos años, desde el verano de 1997 hasta junio -julio de 1999, fueron muchos los rumores, aclaraciones y desmentidos que surgieron en torno al desarrollo por parte de Microsoft de una o más tecnologías destinadas a eludir las imposiciones de Sun en cuanto a Java y, simultáneamente, minar el dominio de dicha plataforma26.

Toda esa información puede clasificarse en una serie de iniciativas, algunas relacionadas entre sí y muchas también con la tecnología Omniware:

• La máquina abstracta universal UVM (Universal Virtual Machine).

• El proyecto Millennium. • El proyecto COOL (C++ Object Oriented Language). • La máquina abstracta CVM (C Virtual Machine /

COOL Virtual Machine) • La plataforma .NET y el lenguaje C#.

25 La patente [WAH97] es solicitada tan sólo 15 meses después de la compra de la empresa; sería interesante saber si Colusa hubiera desarrollado por sí sola la técnica de compresión de código BRISC sin contar con la colaboración de investigadores de Microsoft como Fraser y Evans. 26 Recordemos que desde 1997 hasta enero de 2001 Sun y Microsoft se vieron envueltos en una batalla legal por la compatibilidad del lenguaje Java en sus diferentes implementaciones.

Antes de entrar en detalles se procederá a dar una visión de conjunto sobre tales iniciativas:

• Microsoft tan sólo h a proporcionado información más o menos detallada sobre el proyecto Millennium así como sobre la plataforma .NET y C#; la información sobre el resto de iniciativas apareció de forma puntual en forma de comentarios personales de miembros de la empresa, especulaciones y rumores en prensa.

• El concepto de UVM apareció fugazmente en 1997 relacionándolo con el sistema operativo Windows CE y continuó reapareciendo de forma periódica asociado tanto con COOL como con Omniware.

• Microsoft denominó COOL a dos proyectos aparentemente distintos, uno relacionado con el desarrollo de extensiones para el lenguaje Visual C++ y otro más conceptual referente a nuevos enfoques tecnológicos; sin embargo, ambas líneas podrían estar más relacionadas entre sí de lo que la empresa reconoció en su día. De forma insistente se quiso relacionar Omniware con COOL-enfoque-tecnológico y éste con el Java-killer. Tras el anuncio de .NET muchos fueron los que lo señalaron como el resultado de COOL y/o una emulación de Java, Microsoft negó ambas afirmaciones.

• El proyecto de CVM fue confirmado aunque no aclarado por personal de Microsoft, se confirmó la presencia de Lucco en el mismo y se negó la relación entre COOL y CVM, tampoco se detalló la relación entre la misma y OmniVM. Según diversas fuentes CVM fue presentada en PLDI’99 y aunque se iba a realizar otra demostración en SD’99 fue finalmente cancelada.

• Microsoft no proporcionó en ningún momento datos sobre la evolución que siguió la tecnología Omniware en sus laboratorios y negó cualquier tipo de relación entre Omniware y cualquier proyecto (específicamente con COOL).

En este momento el lector ya se puede comenzar a hacerse una idea sobre la hipótesis sostenida por el autor, la cual queda muy clara al expresarla como una línea temporal (ver Tabla 5): la compra de Colusa y el posterior desarrollo por parte de Microsoft de un producto inacabado, Omniware, junto con el desarrollo de una serie de proyectos paralelos constituyó el caldo de cultivo idóneo para el desarrollo de varias tecnologías 27 alguna de las cuales podría convertirse en una alternativa a la plataforma Java, antigua pretensión de Colusa Omniware y razón de la compra de la empresa.

27 Aunque .NET es la que más relevancia ha alcanzado, CEF (Common Executable Format) también presenta similitudes importantes con Omniware.

Page 12: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

12

Colusa presenta

Omniware

Microsoft compra Colusa

Se completa el desarrollo de Omniware en

Microsoft

1995 1996 1997

Insistentes rumores sobre COOL

Insistentes rumores sobre UVM

Rumores sobre UVM y Windows

CE

1999 1998 1997

Confirmación de CVM Presentación de

.NET y C#

1999 2000

Tabla 5 Proceso de desarrollo propuesto para las tecnologías de Microsoft competidoras de Java.

A continuación se proporcionarán una serie de datos y referencias para dar soporte a esta especulación; el lector podrá así extraer sus propias conclusiones, sin embargo, recuerde que –como dijo A.C. Clarke– la realidad excederá siempre la imaginación más desmedida.

4.1. La máquina abstracta universal (UVM)

El concepto de una máquina abstracta universal no es, en modo alguno, una novedad y tampoco lo era en 1997; una UVM debería permitir desarrollar programas en cualquier lenguaje y que pudiesen ejecutarse de forma equivalente sobre cualquier tipo de hardware. Un de las primeras ocasiones en que se menciona la posibilidad de que Microsoft estuviera desarrollando una máquina virtual con estas características fue en [IDM97]:

[...] el 29 de Septiembre Microsoft revelará su Máquina Abstracta Universal (UVM). La UVM permitirá a aplicaciones desarrolladas con Visual C++, Visual J++ o Visual Basic ejecutarse sin cambios sobre cualquiera de los seis procesadores RISC soportados por Windows CE [...]

Sin embargo, el 29 de septiembre al presentarse Windows CE 2.0 no se menciona en parte alguna la presencia de una máquina abstracta universal, de hecho, según [PRN97]

El Toolkit para sistemas empotrados proporciona a los desarrolladores acceso a todo el potencial de un entorno de desarrollo integrado (IDE) e incluye todos los compiladores cruzados y ensambladores para Windows CE sobre los procesadores soportados.

Podría pensarse que, aún así, la UVM está implícita por el hecho de disponerse de compiladores cruzados; sin embargo, es de suponer que tales herramientas (en realidad extensiones para Visual C++, Visual J++ y Visual Basic) seguirían empleando el lenguaje intermedio utilizado por todos los compiladores de Microsoft enviando la UVM al limbo del que regresaría un año más tarde, esta vez no asociada a Windows CE sino como rival de la JVM.

En 1998 David Smith, director de investigación de Gartner Group, afirmó haber asistido a la demostración de una UVM en una visita a los laboratorios de Microsoft en Redmond. Según la información filtrada dicha máquina abstracta ofrecería

soporte para múltiples lenguajes, entre ellos Java, C y Visual Bas ic; varios medios insinuaron la posible vinculación entre Omniware y la UVM de Smith. Microsoft rechazó hacer ningún comentario sobre el asunto aunque ya en abril de 1997 Michael Toutonghi28, desarrollador en Microsoft, había apuntado la idea en un correo e lectrónico:

Quizás en dos años podríamos tener un API funcional para el desarrollo [de aplicaciones] Internet que rivalice con Java. Puede que para ese momento también tengamos las mejores librerías de clases de una máquina abstracta que soporte Java, VB, un subconjunto de C++, COBOL y Smalltalk.

Desde entonces en ningún momento se ha vuelto a mencionar la máquina abstracta universal aunque, como se verá más adelante, el concepto subyace bajo gran parte de tecnologías finalmente desarrolladas por Microsoft.

4.2. Millennium

En 1998 [COM98] afirmaba que un proyecto que estaba siendo desarrollado por Microsoft, denominado Millennium, empleaba la tecnología de Omniware. Microsoft no ofrece mucha información sobre dicho proyecto lo cual hace difícil establecer una posible relación con OmniVM aunque ésta parece poco probable.

Básicamente, el proyecto Millennium investigaba la forma de desarrollar sistemas verdaderamente distribuidos que fueran auto-organizados y auto-configurables de tal forma que se pudiera dar tanto a los desarrolladores como a las aplicaciones la impresión de que un grupo de ordenadores conectados formando una red constituían una única máquina.

Al amparo de este proyecto se implementaron una serie de prototipos; algunos de los más interesantes fueron Borg, una máquina virtual Java distribuida; Coign, un sistema para producir aplicaciones cliente-servidor a partir de programas COM locales; Continuum, un entorno de ejecución distribuido para COM 2.0 y Millennium Falcon, una implementación de DCOM para redes gigabit.

El lector interesado en el tema puede consultar las referencias incluidas en la bibliografía sobre el proyecto Millennium [NWC98], [MSF01a] y [MSF01b].

4.3. COOL

COOL ha sido una de las iniciativas de Microsoft que más rumores y menos información tangible ha generado; supuestamente se trataba de una alternativa a Java que según algunos involucró a la tecnología Omniware mientras que según otros fue el antecesor de C#. En definitiva, ¿qué fue el proyecto COOL?

En febrero de 1999 la prensa se hizo eco de la intención de Microsoft de abandonar Visual J++ para desarrollar un nuevo

28 Según una circular interna de Microsoft, “Toutonghi ayudó a concebir, establecer la arquitectura y gestionar el Common Language Runtime, el entorno de ejecución, multilenguaje y de altas prestaciones para la iniciativa .NET. Según crecía el proyecto, ayudó a organizar, integrar y, hasta principios de este año [2000], sirvió como General Manager para la plataforma .NET” Por todo ello fue recompensado, junto con otros 15 empleados, con la distinción de Distinguished Engineer; el New York Times se hace eco de la noticia el 3 de Julio de 2000.

Page 13: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

13

lenguaje y/o paradigma similar a Java pero exento de control por parte de Sun, supuestamente COOL utilizaría o se basaría en C++ o en un lenguaje inspirado en C++ y estaría fuertemente vinculado al inminente COM+.

Ciertas fuentes apoyaron estos rumores con unas declaraciones de Greg Leake, product manager en el grupo responsable de Visual Studio; según dichas fuentes, Leake confirmó en todo o en parte los rumores y afirmó:

[COOL] hace la programación en C++ más cómoda. Nos gusta el lenguaje Java porque es simple –y más sencillo que C++– pero tiene que haber formas de simplificarlo. ¿No podemos coger lo bueno que hay en C++ y unirlo con un modelo más simple?

De forma casi simultanea, Microsoft desmentía los rumores y trataba de aclarar el alcance real del proyecto COOL; la postura oficial de la empresa fue la transmitida por Michael Risse, product manager para herramientas de desarrollo. Según esta versión, Microsoft estaba estudiando, desde un punto de vista conceptual, un conjunto de tecnologías que permitieran extender Visual C++ para poder desarrollar COM+; sin embargo, el proyecto no tenía nada que ver con Java ni se había escrito código para el mismo.

En la misma línea se manifestó Jeff Ressler, responsable de Visual C, al ser entrevistado en abril de 1999:

Pregunta: Jeff, dado que ya es de dominio público, qué nos puedes decir sobre el lenguaje de programación COOL? ¿Es una alternativa multiplataforma a Java?

Ressler: COOL es un conjunto de tecnologías que hemos estado evaluando para su inclusión en futuras versiones de Visual C. [...] Nuestro principal objetivo es asegurar que todas nuestras herramientas de desarrollo permiten desarrollar aplicaciones distribuidas de forma flexible y potente... COOL es una reflexión sobre esto.

Como se puede ver, las declaraciones de Leake no se alejaban en absoluto de la “ortodoxia” de Microsoft y tan solo mencionaban Java pero no afirmaban la existencia de una iniciativa similar a Java, obviamente, tampoco la desmentían. COOL fue olvidado hasta que a mediados de 2000 se le vinculó con C#; Microsoft simplemente negó tal relación.

4.4. CVM

El único dato seguro respecto a CVM es que Steve Lucco, antiguo desarrollador de Omniware era el principal responsable del proyecto; según Microsoft, CVM era una máquina abstracta que aceptaba C y no tenía relación alguna con COOL. Así, en la misma entrevista en que Ressler desmentía los rumores sobre la relación de COOL con Java ofreció la versión oficial sobre e ste proyecto:

Pregunta: ¿Qué es ese entorno de ejecución “CVM” en el que está trabajando Microsoft y cuál es su relación con COOL? ¿Es cierto que se basa en la tecnología de Colusa?

Ressler: Existe un proyecto denominado CVM en desarrollo en MS Research y está siendo dirigido por el antiguo vicepresidente de Colusa, Steve Lucco. CVM es uno de los muchos proyectos de investigación sobre

programación y lenguajes que se llevan a cabo en MS Research. Esta iniciativa no tiene nada que ver con el trabajo que estamos haciendo con Visual Studio. CVM, como su propio nombre indica, es una máquina virtual que acepta C.

Sin embargo, es poco probable que CVM fuera un proyecto menos ambicioso que Omniware –OmniVM ya soportaba C y C++ en 1996– con lo cual es presumible que CVM aceptara un lenguaje derivado de C++ o bien varios lenguajes. Así pues, nos parece poco creíble la afirmación de Ressler y nos inclinamos por la hipótesis original según la cual CVM sería el acrónimo de COOL Virtual Machine; si esta teoría fuese cierta, Microsoft estaba desarrollando un lenguaje inspirado en C++ y una máquina virtual para el mismo (en realidad para su código intermedio).

En mayo de ese mismo año iban a producirse dos acontecimientos en los que debía aparecer Steve Lucco: PLDI’9929 y SD’9930.

En PLDI Lucco dirigió un tutorial sobre el diseño e implementación eficiente de máquinas abstractas; en el mismo expuso las características más relevantes de la CVM y, parece ser, realizó una demostración de la misma ejecutando una versión de MS-Word que fue traducida a código nativo en 0,25s; según dichas fuentes, Lucco afirmó que la máquina podía traducir a una velocidad de entre 10 y 12 MB/s 31.

Apenas una semana después se celebraba SD’99, durante todo el mes de abril se habló de la presentación de la CVM que iba a hacer Lucco:

[...] incorpora aislamiento de fallos, bytecode y técnicas de compresión de código que eliminan muchos de los problemas de rendimiento que impiden una mayor difusión de las plataformas basadas en máquinas virtuales. Como principal responsable de CVM, Steve [Lucco] está en una posición inmejorable para describir cómo estas nuevas tecnologías van a afectar la evolución de las máquinas abstractas.

Sin embargo, Microsoft canceló finalmente la presentación y CVM cayó en el olvido.

4.5. .NET

4.5.1. ¿Fue Omniware un fracaso?

A la vista de lo anterior, ¿qué se puede decir sobre el trabajo desarrollado por Microsoft con Omniware? Lo cierto es que el proyecto más relacionado con la misma, CVM, desapareció sin dejar rastro y los antiguos desarrolladores de Omniware incorporados a Microsoft abandonaron la empresa. ¿Fue ese esfuerzo un fracaso? Más bien lo contrario.

Es muy posible que la intención inicial de Microsoft fuera la de comprar un producto externo, Omniware, para mejorarlo ligeramente y lanzarlo de forma inmediata como alternativa a Java; sin embargo, conforme la investigación y el desarrollo del mismo avanzaban más se alejaba la tecnología de Colusa de

29 1999 ACM SIGPLAN Conference on Programming Language Design and Implementation. 30 Software Development’99. 31 Microsoft OmniVM podía traducir código a más de 2,5 MB/s según [ERN97].

Page 14: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

14

ser un producto para ser un medio de aprendizaje y experimentación y, en ese sentido, sí fue un éxito.

Está claro que Omniware, COOL y CVM no existen bajo la forma de ningún tipo de producto; sin embargo, no es descabellado afirmar que lo esencial de las mismas se manifiesta de forma muy clara en varias tecnologías Microsoft, especialmente .NET, tal y como se mostrará a continuación.

4.5.2. Paralelismos entre .NET y Omniware

En este punto se describirán algunos aspectos relevantes de la plataforma .NET que han podido inspirarse en Omniware; para evitar suspicacias las únicas fuentes que se citarán en este punto serán las “canónicas”, esto es los documentos de Microsoft sobre .NET.

Según Microsoft, .NET ofrece una potente plataforma para el desarrollo de aplicaciones Internet que pueden ejecutarse sobre cualquier dispositivo .NET. El núcleo de esta plataforma es el CLR (Common Language Runtime). El CLR proporciona una forma independiente del dispositivo y del lenguaje para expresar los datos y el comportamiento de las aplicaciones. Aunque el CLR soporta principalmente lenguajes orientados a objetos, también admite lenguajes funcionales y procedurales. A través del CLR los lenguajes pueden operar entre sí y hacer uso de un recolector de basura, un sistema de seguridad, soporte de excepciones y un potente marco de desarrollo.

Microsoft IL es el lenguaje intermedio generado por todos los compiladores que tengan como destino el CLR. El CLR convierte los binarios IL, independientes del dispositivo, a código nativo utilizando compiladores de IL a código nativo (conocidos incorrectamente como JIT). Estos compiladores pueden ser utilizados en modo Just-In-Time (JIT) convirtiendo los métodos de IL a código nativo antes de ser ejecutados por primera vez. También pueden utilizarse para convertir un módulo completo a código nativo y almacenar el código nativo para uso futuro. Aunque es posible interpretar código IL, el entorno de ejecución nunca interpreta IL sino que siempre lo compila a código nativo. [MSF00b]

Los dos párrafos anteriores son una breve introducción de Microsoft a la tecnología .NET: plataforma para desarrollo de aplicaciones Internet, entorno de ejecución independiente del lenguaje y del hardware, sistema de seguridad, lenguaje intermedio para el entorno de ejecución, nunca interpretado sino traducido a código nativo... Si se sustituye .NET por Omniware, CLR por OmniRun, IL por código Omniware y compilador de IL por traductor OmniVM uno podría estar leyendo el manual de usuario de Omniware 2000; obviamente, no somos tan simplistas, .NET no es Omniware, ahora bien, su filosofía, conceptos fundamentales y arq uitectura básica es debida en un elevadísimo grado a Omniware.

A continuación trataremos las principales características de .NET comparándolas con las prestaciones ofrecidas por Omniware: el entorno de ejecución y el sistema de protección.

Entorno de ejecución .NET denomina CLR (Common Language Runtime) a su

entorno de ejecución, nosotros afirmamos que dicho CLR es virtualmente equivalente al entorno de ejecución de Omniware o, si se quiere, su heredero directo. ¿Cuáles son sus características para hacer semejante afirmación?

Según [MSF00a]: El entorno .NET proporciona un CLR (Common Language Runtime) que gestiona la ejecución del código fuente tras haber sido compilado a código intermedio

(MSIL), OptIL32, o código nativo. Todo el código basado en MSIL u OptIL se ejecuta como código gestionado; es decir, es código que se ejecuta bajo un “contrato de cooperación” con .NET. [...]

Una característica clave del CLR es su capacidad para proporcionar aislamiento por software a programas ejecutándose en un espacio de direcciones único. Hace esto asegurando acceso seguro a todas las áreas de memoria al ejecutar código gestionado seguro. Algunos compiladores pueden generar MSIL que no sólo es seguro sino cuya seguridad puede ser probada al examinar su código. [...]

El CLR proporciona los siguientes servicios: gestión del código, aislamiento de memoria por software, verificación de la seguridad del código MSIL, conversión de MSIL a código nativo, carga y ejecución de código gestionado, acceso a metadatos, gestión de memoria, inserción y ejecución de chequeos de seguridad, gestión de excepciones [...]

Una de las funciones más importantes del CLR es la conversión “al vuelo” de MSIL (u OptIL) a código nativo. Los compiladores de código fuente generan MSIL (u OptIL) y los “compiladores” JIT convierten ese código MSIL a código nativo de arquitecturas específicas [...]

Las similitudes entre CLR y el entorno de ejecución Omniware resultan patentes, máxime si se comparan con las características de Microsoft Omniware, ver apartado 3.3; tanto CLR como Omniware coinciden en lo fundamental:

• Son independientes del lenguaje fuente puesto que se basan en un código intermedio.

• Pueden ejecutar programas de forma segura en un espacio de direcciones único.

• Los compiladores pueden introducir las medidas de protección en los programas.

• Pueden comprobar la seguridad del código por su simple examen.

• Traducen el código intermedio a código nativo específico del host destino.

Sistema de protección .NET dispone de aislamiento de memoria por software

(otra forma de denominar la técnica de SFI), la técnica utilizada por .NET se basa en dos conceptos básicos –ya existentes en Omniware– verificación y dominios de aplicación.

Según [MSF00a], Los programas seguros referencian únicamente memoria que ha sido asignada para su uso, además acceden a los objetos sólo a través de sus interfaces públicos. Estas dos restricciones permiten a los objetos utilizar de forma segura un espacio de direcciones único [...] El mecanismo de seguridad del CLR sólo puede evitar de forma efectiva el acceso no autorizado si se puede verificar que el código es seguro.

Para lograr esto [...] chequea que los metadatos están bien formados y lleva a cabo análisis de control de flujo para asegurar que se cumplen ciertas condiciones. El entorno de

32 OptIL es un subconjunto de MSIL que permite generar código nativo optimizado a gran velocidad. Según [MSF00a] “OptIL es útil en situaciones donde el tiempo y la memoria son escasos durante la conversión a código nativo pero el código nativo producido debe alcanzar un rendimiento óptimo”; si existe relación entre OptIL y BRISC es algo difícil de decir.

Page 15: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

15

ejecución declara que un programa ha sido verificado sólo si es seguro [según esos criterios].

Como recordará el lector, MS-Omniware ya realizaba algo muy similar, si el módulo estaba “certificado” por el compilador comprobaba que dicha certificación fuera válida, lo verificaba, declarándolo seguro o inseguro.

Por lo que respecta a los dominios de aplicación son equivalentes en todo a los dominios de fallo de [WAH93] o a los dominios de protección de Omniware; como muestra la descripción ofrecida por [MSF01c]:

Los dominios de aplicación se benefician de las características de seguridad de la plataforma .NET y constituyen la unidad mínima para la política de seguridad. Los dominios de aplicación también proporcionan tolerancia a fallos con un coste menor que en los procedimientos tradicionales. De hecho, múltiples dominios de aplicación pueden ejecutarse en un único proceso Windows.

Los sistemas operativos y entornos de ejecución proporcionan alguna forma de aislamiento entre aplicaciones ejecutándose en el sistema [...]

[…] En muchos aspectos, los dominios de aplicación del CLR equivalen a procesos Windows.

El entorno de ejecución crea un dominio de aplicación para cada aplicación en ejecución [...] Los dominios de aplicación aíslan aplicaciones separadas ejecutándose dentro del mismo proceso [...]

No hay llamadas directas entre objetos en diferentes dominios de aplicación, en cambio, es necesario usar un intermediario (proxy)[...]

Como se puede ver, no hay diferencias entre dominios de aplicación .NET y dominios de protección Omniware: proporcionan un esquema semejante a la segmentación de memoria de un sistema operativo, un solo proceso real puede contener varios dominios, los dominios aíslan a las aplicaciones que se ejecutan en cada dominio, además, no se permiten llamadas directas entre dominios, en su lugar se requiere un proxy en .NET o una llamada segura entre dominios de fallo (cross-fault domain RPC) en Omniware.

4.5.3. La originalidad de Omniware

A pesar de las similitudes, .NET no es una evolución lineal de Omniware; existen varios aspectos en los que difieren radicalmente no siendo el menos importante de todos el lenguaje intermedio. Como se dijo, el código Omniware era básicamente un lenguaje ensamblador para una máquina RISC mientras que MSIL es un juego de instrucciones para una máquina de pila [...] hay también un conjunto de instrucciones para implementar aspectos de orientación a objetos [...] [MSF00a]33. Así pues, ambos lenguajes difieren de forma dramática lo cual hace bastante difícil que .NET sea un producto derivado de Omniware -el-código, aunque es innegable que es una tecnología fuertemente inspirada en Omniware-la-idea.

Por tanto, .NET no es una tecnología revolucionaria sino una revisión de ideas ajenas que, como veremos a continuación, tampoco constituyeron una novedad extraordinaria en su momento. Simplificando en extremo, se puede decir que Colusa Omniware se reducía a los siguientes

33 Descripción totalmente análoga al juego de instrucciones de la máquina virtual Java.

conceptos: máquinas abstractas, segmentación de memoria, lenguajes intermedios y generación de código “al vuelo”; ninguno de los cuales fue introducido ni mejorado de forma sustancial por los desarrolladores de OmniVM:

• La idea de las máquinas abstractas data de mediados de los años 60 con lo cual ni OmniVM ni JVM inventaron nada al introducir una máquina virtual.

• La técnica de segmentación de memoria y los fallos por accesos ilegales a dichos segmentos es común en los sistemas operativos y data de la década de los 70.

• La utilización de un código intermedio universal fue propuesta en 1958 con el UNCOL y resurgió a principios de los años 90 con ANDF; tanto el código Omniware como el bytecode de Java son deudores de esta idea.

• La generación de código “al vuelo” es algo más reciente puesto que hasta que no existieron procesadores suficie ntemente rápidos era prácticamente irrealizable. Sin embargo, tampoco esta idea fue gestada por los desarrolladores de Omniware ni, por si se lo pregunta, de Sun. Al lector interesado en el tema le recomendamos la lectura de alguno de los trabajos de Michael Franz; este investigador suizo viene trabajando desde 1990 en sistemas de código móvil, en [FRA91] presenta unos resultados iniciales que desembocarían en 1994 en su tesis doctoral, “Code Generation On the Fly: A Key to Portable Software” [FRA94], dirigida por Niklaus Wirth. El sistema desarrollado por Franz compilaba el lenguaje Oberon a un código intermedio comprimido que era traducido a código nativo en tiempo de carga... Curioso, ¿verdad? Dicho sistema creció hasta convertirse en Juice (http://caesar.ics.uci.edu/juice/), otro sistema de código móvil “competidor” de Java.

En resumen, si .NET no es una novedad, Colusa Omniware, su antecesor, tampoco lo fue en su momento; simplemente fue uno más de los muchos proyectos que se estaban llevando a cabo para lograr una plataforma “universal” con la salvedad de que Microsoft se percató de su existencia y decidió adquirirlo.

5. Conclusión

Como ya se dijo anteriormente, la finalidad que se perseguía con los datos anteriores era doble: por un lado, permitir al lector extraer sus propias conclusiones respecto a la estrategia de Microsoft para el desarrollo Internet y, por otro, servir como soporte para la hipótesis del autor.

Esperamos haber logrado el primero de tales objetivos y, respecto al segundo, aún cuando nuestra hipótesis no puede ser demostrada y alcanzar el honorable grado de tesis ofreceremos una opinión igualmente difícil de rebatir.

En agosto de 1995, de forma casi simultanea a Java, una pequeña empresa, Colusa Software, lanza su único producto, Omniware.

Omniware era similar a Java puesto que permitía desarrollar código móvil que podía ser descargado por una red y ser ejecutado de forma segura en el host destino; hasta ahí el parecido, Omniware presentaba dos diferencias realmente interesantes: no introducía un nuevo lenguaje de programación, aceptaba inicialmente C y C++ y, potencialmente, cualquier lenguaje tradicional; por otra parte, el código móvil no era

Page 16: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

16

interpretado sino traducido a código nativo y ejecutado con lo cual era bastante más rápido que las versiones iniciales de Java.

Microsoft intuye una oportunidad única y cuando Omniware llevaba siete meses en el mercado, en marzo de 1996, compra Colusa por una cantidad nunca desvelada incorporándose los desarrolladores de Omniware como personal de investigación de Microsoft.

En poco más de un año, Omniware finaliza su evolución dentro de Microsoft dotándose de un sistema de seguridad completo, mejorando las prestaciones y reduciéndose el tamaño de los módulos móviles al incorporar un sistema de compresión de código.

Microsoft, sin embargo, nunca saca al mercado un producto con las características de Omniware; probablemente, considera más valioso proseguir investigando hasta obtener tecnologías que puedan enfrentarse con garantías a una plataforma Java cada vez más consolidada.

Mientras los investigadores trabajan en diversos proyectos, Microsoft es demandada judicialmente por Sun por un presunto incumplimiento de contrato al introducir unilateralmente modificaciones sobre el lenguaje Java que hacían incompatibles los programas desarrollados con herramientas de terceros con la máquina virtual Java de Microsoft. En esos momentos todos los rumores apuntan al abandono de la tecnología Java por parte de Microsoft y el contraataque con una plataforma nueva q ue la pudiese englobar, superar y, tal vez, eliminar a la misma.

Muchos proyectos que estaban en curso en aquellos momentos fueron relacionados de una forma u otra con la búsqueda de un Java-killer o con la cuasi-olvidada tecnología Omniware. Algunos de lo s más relevantes (ya fueran reales o ficticios) fueron la búsqueda de una máquina virtual universal (multilenguaje), Millennium, COOL y CVM (COOL Virtual Machine o C Virtual Machine).

Microsoft negó, matizó o confirmó de forma escueta dichos proyectos, sie mpre afirmando que ninguno de ellos estaba relacionado entre sí ni con Java o tecnologías similares.

La UVM parecía la más seria amenaza a la JVM, sin embargo, nunca pudo ser confirmada ni fue presentada en público.

Millennium era un proyecto de investigación dedicado al desarrollo de sistemas verdaderamente distribuidos que, aparentemente, tenía poco o nada que ver con Java o con Omniware.

COOL fue un proyecto de investigación pura destinado a extender Visual C++ para facilitar el desarrollo de aplicaciones Internet mediante COM+ y/o un nuevo paradigma similar a Java con un lenguaje basado en C++.

CVM fue relacionada COOL aunque Microsoft negó tal extremo al afirmar que era una máquina virtual que aceptaba C, también confirmó que uno de los desarrolladores de Omniware estaba al frente del proyecto. Parece ser que CVM fue demostrada en un congreso en 1999 aunque una presentación previamente anunciada y con mayor publicidad fue finalmente cancelada.

El antiguo personal de Colusa deja Microsoft de forma paulatina aunque es seguro que Steve Lucco, responsable de la CVM, permanece, al menos, hasta la segunda mitad de 1999.

A mediados del año 2000 se comienza a hablar de una nueva tecnología de Microsoft, las características de la misma recuerdan en gran medida las supuestas para COOL. Al

presentarse .NET y C# Microsoft niega con la misma intensidad que esté relacionado con COOL como que sea un duplicado de la plataforma Java; hasta donde sabemos, nadie relaciona .NET directamente con Omniware aunque, como hemos tra tado de argumentar, Omniware constituyó la base inicial para el desarrollo conceptual de .NET.

En resumen, .NET la revolución tecnológica para el siglo XXI hunde sus raíces en Omniware que era, a su vez, una revisión de ideas de los años 60, 70 y 80 para su adaptación a Internet, red heredera de un proyecto militar de los años 70... Verdaderamente, no hay nada nuevo bajo el sol.

Referencias

[ALI96] Ali-Reza Adl-Tabatabai, Geoff Langdale, Steven Lucco y Robert Wahbe. Efficient and Language-Independent Mobile Programs. Proceedings of the ACM SIGPLAN’96 Conference on Programming Language Design and Implementation, pp. 127-136, Mayo, 1996.

[COL95] Colusa Software. Colusa Software White Paper. Omniware Technical Overview, 1995.

[COM98] Computer Weekly. Sun seeks to put Gates in hot spot. http://www.findarticles.com/cf_0/m0COW/1998_Dec_10/53441977/print.jhtml. Diciembre, 1998.

[ERN97] Jens Ernst, William Evans, Christopher W. Fraser, Steven Lucco, Todd A. Proebsting. Code Compression. Proceedings of the ACM SIGPLA N’97 Conference on Programming Language Design and Implementation, pp. 358-365, Junio, 1997.

[FRA91] Michael Franz y S. Ludwig. Portability Redefined. Proceedings of the 2nd International Modula -2 Conference, Loughborough, England, Septiembre, 1991.

[FRA94] Michael Franz. Code Generation On the Fly: A Key for Portable Software. Tesis Doctoral, Institute for Computer Systems, ETH Zurich, 1994.

[IDM97] Intranet Design Magazine. News digest: July 17, 1997. http://idm.internet.com/idm/vol2/15/in -news.html, Julio, 1997.

[LUC95] Steven Lucco, Oliver Sharp, Robert Wahbe. Omniware: A Universal Substrate for Web Programming. Fourth International World Wide Web Conference, MIT, Diciembre, 1995.

[MSF96] Microsoft Corp. Microsoft Broadens Active Internet Vision Through Agreements With Colusa, Aspect, nCompass and Citrix. http://www.microsoft.com/presspass/press/1996/mar96/acquirpr.asp , Marzo, 1996.

[MSF00a]Microsoft Corp. .NET Framework Common Language Runtime Architecture, Microsoft Corp., Junio, 2000.

[MSF00b]Microsoft Corp. The IL Assembly Language Programmers’ Reference, Microsoft Corp., Noviembre, 2000.

[MSF01a]Microsoft Corp. MSR Millennium Project. http://research.microsoft.com/sn/millennium/, 2001.

Page 17: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

17

[MSF01b]Microsoft Corp. Coign. http://research.microsoft.com/sn/millennium/coign.asp, 2001.

[MSF01c]Microsoft Corp. Application Domains. http://msdn.microsoft.com/library/dotnet/cpguide/cpconapplicationdomains.htm, 2001.

[NWC98] Network Computing. Java Brews Up a Storm in the Enterprise. Christy Hudgins-Bonafield interviewed Galen Hunt and Yi-Min Wang. http://networkcomputing.com/shared/printArticle?article=nc/919/919f1side10.html&pub=nwc, Octubre, 1998.

[PRN97] PRNewswire. Microsoft Announces the Windows CE Embedded Toolkit for Visual C++ 5.0. http://www.prnewswire.com/cgi-bin/stories.pl?ACCT=105&STORY=/www/story/9-29-97/325914, Septiembre, 1997.

[SMA96] Christopher Small y Margo Seltzer. A comparison of OS extension technologies. Proceedings of the 1996 USENIX Conference, pp. 41-54, Enero, 1996.

[WAH93]Robert Wahbe, Steven Lucco, Thomas E. Anderson, Susan L. Graham. Efficient Software-Based Fault Isolation. 14th ACM Symposium on Operating Systems Principles, Ashville, NC, pp. 203-216, Diciembre, 1993.

[WAH95]Robert S. Wahbe, Steven E. Lucco. Methods for safe and efficient implementations of virtual machines. U.S. Pat. 5761477, Microsoft Corp., Diciembre, 1995.

[WAH97]Robert S. Wahbe, Steven E. Lucco, Christopher W. Fraser, Vance Palmer Morrison. Safe general purpose virtual machine computing system . U.S. Pat. 6151618, Microsoft Corp., Junio, 1997.

Page 18: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

18

Apéndice: Juego de instrucciones de OmniVM

Notación utilizada rd Destino: registro entero.

shimm Inmediato de 5 bits sin signo para desplazamiento.

rs1 Fuente: registro entero.

o Desplazamiento de 16 bits con signo para cálculo

de direcciones. rs2 Fuente: registro entero.

sym26 Índice de 26 bits de un símbolo en la tabla de

símbolos. f d Destino: registro flotante.

sym32 Índice de 32 bits de un símbolo en la tabla de

símbolos. fs1 Fuente: registro flotante.

pcre130 Desplazamiento de 30 bits con signo relativo al

contador de programa. fs2 Fuente: registro flotante.

pcre126 Desplazamiento de 26 bits con signo relativo al

contador de programa. imm16 Inmediato de 16 bits con signo.

c1 Constante de 5 bits para indicar bit de comienzo

en un campo de bits. imm32 Inmediato de 32 bits con signo.

c2 Constante de 5 bits para indicar tamaño de un

campo de bits. aux32 Operando inmediato de 32 bits entero con signo.

double Constante de doble precisión.

Modos de direccionamiento Inmediato Ej: add.i rd, rs1, imm16 Directo Ej: add.i rd, rs1 , rs2 Desplazamiento de registro Ej: ld.d fd, o(rs1) Registro indexado Ej: ld.d fd, [rs1 + rs2] Símbolo + desplazamiento Ej: ld.i8 rd, sym32 + o Símbolo + registro Ej: ld.i8, rd, sym32(rs1) Símbolo + desplazamiento + registro Ej: ld.i8 rd, sym32 + o(rs1)

Formato de las instrucciones

Instrucciones de 32 bits REG32

op rd sbop rs1 rs2 sftam

6 5 5 5 5 5 1

Instrucciones de este tipo:

• Todas las instrucciones con modo de a cceso por registro indexado.

• Todas las instrucciones con modo de acceso directo (que no coincidan con otra categoría posterior).

IMM32

op rd rs1 imm16

6 5 5 16

Instrucciones de este tipo:

• Operaciones aritmético-lógicas enteras (exceptuando división y multiplicación) con un parámetro inmediato.

• Todas las instrucciones con modo de acceso por desplazamiento de registro que tengan0como operandos enteros de 8 y 32 bits.

• Las siguientes instrucciones:

o mov.i rd, imm16

JUMP32

op pcre

6 26

Instrucciones de este t ipo:

• Todas las instrucciones de salto o llamada con parámetros de 26 bits (sym26 o pcre126)

Instrucciones de 64 bits EXT64

op rd sbop imm16 rs1

6 5 5 16 5 27

Instrucciones de este tipo:

Page 19: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

19

• División y multiplicación enteras con un parámetro inmediato.

• Todas las instrucciones con modo de acceso por desplazamiento de registro que tengan como operandos enteros de 16 bits, flotantes y dobles.

IMM64

op rd sbop imm16 imm32

6 5 5 16 32

Instrucciones de este tipo:

• Todas las instrucciones con modo de acceso por símbolo más desplazamiento.

REG64

op rd sbop rs1 rs2 imm32

6 5 5 5 5 6 32

Instrucciones de este tipo:

• Todas las instrucciones de bifurcación.

• Todas las instrucciones con modo de acceso por símbolo más registro.

• Las siguientes instrucciones:

o mov.f fd, fimm o cnst rd, imm32 o movb rd, rs1, len o lcall sym32

BF64

op rd sbop imm16 rs1 c1 c2

6 5 5 16 5 5 5 1 16

Instrucciones de este tipo:

• Instrucciones de inserción/extracción de bits.

Instrucciones de 96 bits REG96

op rd sbop rs1 rs2 imm32 aux32

6 5 5 5 5 6 32 32

Instrucciones de este tipo:

• Todas las instrucciones con modo de acceso por símbolo más desplazamiento más registro.

DOUBLE96

op rd sbop rs1 rs2 double

6 5 5 5 5 6 64

Instrucciones de este tipo:

• Las siguientes instrucciones:

o mov.d fd, fimm

Page 20: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

20

Lista de instrucciones

Instrucciones aritméticas Sumar

Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. add.i rd, rs1, rs2 enteros, directo REG32 00 add.i rd, rs1, imm16 enteros, inmediato IMM32 01 add.f fd, fs1, fs2 f lotantes, directo REG32 71 000 00 add.d fd, fs1, fs2 dobles, directo REG32 71 000 01

Restar Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. sub.i rd, rs1, rs2 enteros, directo REG32 06 sub.i rd, rs1, imm16 enteros, inmediato IMM32 07 sub.i rd, imm16, rs1 enteros, inmediato IMM32 77 sub.f fd, fs1, fs2 flotantes, directo REG32 71 000 10 sub.d fd, fs1, fs2 dobles, directo REG32 71 000 11

Menos unario Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. neg rd, rs1 entero, directo REG32 60 fneg.f fd, fs1 flotante, directo REG32 71 010 00 fneg.d fd, fs1 doble, directo REG32 71 010 01

Multiplicar Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. mul.i rd, rs1, rs2 enteros, directo REG32 70 110 00 mul.i rd, rs1, imm16 enteros, inmediato EXT64 70 110 10 mul.f fd, fs1, fs2 flotantes, directo REG32 71 001 00 mul.d fd, fs1, fS2 dobles, directo REG32 71 001 01

Dividir Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. div.i rd, rs1, rs2 enteros, directo REG32 70

34

div.i rd, rs1, imm16 enteros, inmediato EXT64 70 div.f fd, fs1, fs2 flotantes, directo REG32 71 001 10 div.d fd, fs1, fs2 dobles, directo REG32 71 001 11

Módulo Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. mod.i rd, rs1, rs2 enteros, directo REG32 70 100 00 mod.i rd, rs1, imm16 enteros, inmediato EXT64 70 100 01

Valor absoluto Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. fabs.f fd, fs1 flotante, directo REG32 71 010 10 fabs.d fd, fs1 doble, directo REG32 71 010 11

Raíz cuadrada Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. fsqrt.f fd, fs1 flotante, directo REG32 71 011 00 fsqrt.d fd, fs1 doble, directo REG32 71 011 01

Instrucciones lógicas AND a nivel de bits

Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. and rd, rs1, rs2 enteros, directo REG32 02 and rd, rs1, imm16 enteros, inmediato IMM32 03

OR inclusivo a nivel de bits Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. ior rd, rs1, rs2 enteros, directo REG32 04 ior rd, rs1, imm16 enteros, inmediato IMM32 05

OR exclusivo a nivel de bits Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. xor rd, rs1, rs2 enteros, directo REG32 66 xor rd, rs1, imm16 enteros, inmediato IMM32 67

NOT a nivel de bits Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. not rd, rs1 Entero, directo REG32 61

34 Todas las instrucciones con opcode 70 van acompañadas de un subopcode, ver los comentarios al juego de instrucciones al final de este apéndice.

Page 21: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

21

Instrucciones de desplazamiento Desplazar bits a la izquierda

Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. sl rd, rs1, rs2 entero, directo REG32 13 sl rd, rs1, shimm entero, inmediato REG32 20

Desplazar bits a la derecha Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. sr.i rd, rs1, rs2 entero, directo REG32 11 sr.i rd, rs1, shimm entero, inmediato REG32 12 sr.u rd, rs1, rs2 entero sin signo, directo REG32 21 sr.u rd, rs1, shimm entero sin signo, inmediato REG32 22

Instrucciones de control Llamar a subrutina

Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. call rd directo REG32 52 call sym26 símbolo JUMP32 30

35

call pcre126 relativo al contador de programa JUMP32 54

Llamar a subrutina (llamada lejana) Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. lcall sym32 símbolo REG64 70 101 10

Saltar incondicionalmente Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. jmp sym26 símbolo JUMP32 32

Saltar incondicionalmente (salto lejano) Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. ljmp sym32 símbolo REG32 70 101 01

Volcar un registro Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. spill.i rd, o(rs1) entero, desplazamiento de registro IMM32 50 spill.f fd, o(fs1) flotante, desplazamiento de registro EXT64 71 111 00 spill.d fd, o(fs1) doble, desplazamiento de registro EXT64 71 111 01

Recargar un registro Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. reload.i rd, o(rs1) entero, desplazamiento de registro IMM32 51 reload.f fd, o(rs1) flotante, desplazamiento de registro EXT64 71 111 10 reload.d fd, o(rs1) doble, desplazamiento de registro EXT64 71 111 11

Retorno de subrutina (?)36 Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. ijr rd entero, directo REG32 75 rjr rd entero, directo REG32 74 tjr rd entero, directo REG32 31

35 El opcode 30 está repetido para las instrucciones call sym26 y ld.i8 rd, [rs1+rs2], ver los comentarios al juego de instrucciones al final de este apéndice. 36 [ERN97] muestra un ejemplo de generación de código Omniware a partir de código fuente C; en dicho código la instrucción rjr aparenta ser el punto de retorno de una subrutina:

Código C int salt (int j, it i) { if (j > 0) { pepper (i, j); j--; } return j; }

Código Omniware enter sp,sp,24 spill.i n4,16(sp) spill.i ra,20(sp) mov.i n4,n0 mov.i n2,n1 ble.i n4,0,$L56 mov.i n1,n4 mov.i n0,n2 call _pepper $L56: add.i n0,n4,-1 reload.i n4,16(sp) reload.i ra,20(sp) exit sp,sp,24 rjr ra

Page 22: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

22

Instrucciones de bifurcación Bifurcar si iguales

Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. beq.i rs1, rs2, pcre130 enteros, directo REG64 33 beq.f fs1, fs2, pcre130 flotantes, directo REG64 73 010 00 beq.d fs1, fs2, pcre130 dobles, directo REG64 73 010 01

Bifurcar si distintos Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. bne.i rs1, rs2, pcre130 enteros, directo REG64 40 bne.f fs1, fs2, pcre130 flotantes, directo REG64 73 010 10 bne.d fs1, fs2, pcre130 dobles, directo REG64 73 010 11

Bifurcar si menor o igual Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. ble.i rs1, rs2, pcre130 enteros, directo REG64 41 ble.u rs1, rs2, pcre130 enteros sin signo, directo REG64 47 ble.f fs1, fs2, pcre130 flotantes, directo REG64 73 011 00 ble.d fs1, rs2, pcre130 dobles, directo REG64 73 011 01

Bifurcar si menor Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. blt.i rs1, rs2, pcre130 enteros, directo REG64 42 blt.u rs1, rs2, pcre130 enteros sin signo, directo REG64 57 blt.f fs1, fs2, pcre130 flotantes, directo REG64 73 011 10 blt.d fs1, fs2, pcre130 dobles, directo REG64 73 011 11

Bifurcar si mayor o igual Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. bge.i rs1, rs2, pcre130 enteros, directo REG64 43 bge.u rs1, rs2, pcre130 enteros sin signo, directo REG64 45 bge.f fs1, fs2, pcre130 flotantes, directo REG64 73 100 00 bge.d fs1, fs2, pcre130 dobles, directo REG64 73 100 01

Bifurcar si mayor Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. bgt.i rs1, rs2, pcre130 enteros, directo REG64 44 bgt.u rs1, rs2, pcre130 enteros sin signo, directo REG64 46 bgt.f fs1, fs2, pcre130 flotantes, directo REG64 73 100 10 bgt.d fs1, fs2, pcre130 dobles, directo REG64 73 100 11

Instrucciones de carga y almacenamiento Cargar

Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. ld.i8 rd, o(rs1) entero 8 bits, desplazamiento de registro IMM32 26 ld.i8 rd, [rs1 + rs2] entero 8 bits, registro indexado REG32 30 ld.i8 rd, sym32 + o entero 8 bits, símbolo + desplazamiento IMM64 64 ld.i8 rd, sym32(rs1) entero 8 bits, símbolo + registro REG64 70 111 10 ld.i8 rd, sym32 + o(rs1) entero 8 bits, símbolo + desplazamiento + registro REG96 72 100 10 ld.i16 rd, o(rs1) entero 16 bits, desplazamiento de registro EXT64 72 000 00 ld.i16 rd, [rs1 + rs2] entero 16 bits, registro indexado REG32 72 001 00 ld.i16 rd, sym32 + o entero 16 bits, símbolo + desplazamiento IMM64 72 010 00 ld.i16 rd, sym32(rs1) entero 16 bits, símbolo + registro REG64 72 011 00 ld.i16 rd, sym32 + o(rs1) entero 16 bits, símbolo + desplazamiento + registro REG96 72 101 10 ld.iw rd, o(rs1) entero 32 bits (palabra), desplazamiento de registro IMM32 14 ld.iw rd, [rs1 + rs2] entero 32 bits (palabra), registro indexado REG32 15

37

ld.iw rd, sym32 + o entero 32 bits (palabra), símbolo + desplazamiento IMM64 24 ld.iw rd, sym32(rs1) entero 32 bits (palabra), símbolo + registro REG64 65 ld.iw rd, sym32 + o(rs1) entero 32 bits (palabra), símbolo + desplazamiento + registro REG96 72 100 00 ld.u8 rd, o(rs1) entero sin signo 8 bits, desplazamiento de registro IMM32 36 ld.u8 rd, [rs1 + rs2] entero sin signo 8 bits, registro indexado REG32 37 ld.u8 rd, sym32 + o entero sin signo 8 bits, símbolo + desplazamiento IMM64 55 ld.u8 rd, sym32(rs1) entero sin signo 8 bits, símbolo + registro REG64 70 111 11 ld.u8 rd, sym32 + o(rs1) entero sin signo 8 bits, símbolo + desplazamiento + registro REG96 72 101 00 ld.u16 rd, o(rs1) entero sin signo 16 bits, desplazamiento de registro EXT64 72 000 10 ld.u16 rd, [rs1 + rs2] entero sin signo 16 bits, registro indexado REG32 72 000 10 ld.u16 rd, sym32 + o entero sin signo 16 bits, símbolo + desplazamiento IMM64 72 010 10 ld.u16 rd, sym32(rs1) entero sin signo 16 bits, símbolo + registro REG64 72 011 10 ld.u16 rd, sym32 + o(rs1) entero sin signo 16 bits, símbolo + desplazamiento + registro REG96 72 110 00 ld.f fd, o(rs1) flotante, desplazamiento de registro EXT64 71 110 00 ld.f fd, [rs1 + rs2] flotante, registro indexado REG32 71 110 10 ld.f fd, sym32 + o flotante, símbolo + desplazamiento IMM64 70 001 00 ld.f fd, sym32(rs1) flotante, símbolo + registro REG64 70 001 10

37 El opcode 15 está repetido para las instrucciones ld.iw rd, [rs1+rs2] y st.iw rd, o(rs1), ver los comentarios al juego de instrucciones al final de este apéndice.

Page 23: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

23

ld.f fd, sym32 + o(rs1) flotante, símbolo + desplazamiento + registro REG96 72 110 01 ld.d fd, o(rs1) doble, desplazamiento de registro EXT64 71 110 01 ld.d fd, [rs1 + rs2] doble, registro indexado REG32 71 110 11 ld.d fd, sym32 + o doble, símbolo + desplazamiento IMM64 70 001 01 ld.d fd, sym32(rs1) doble, símbolo + registro REG64 70 001 11 ld.d fd, sym32 + o(rs1) doble, símbolo + desplazamiento + registro REG96 72 110 10

Almacenar Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. st.i8 rd, o(rs1) entero 8 bits, desplazamiento de registro IMM32 27 st.i8 rd, [rs1 + rs2] entero 8 bits, registro indexado REG32 35 st.i8 rd, sym32 + o entero 8 bits, símbolo + desplazamiento IMM64 72 111 01 st.i8 rd, sym32(rs1) entero 8 bits, símbolo + registro REG64 72 111 10 st.i8 rd, sym32 + o(rs1) entero 8 bits, símbolo + desplazamiento + registro REG96 72 100 11 st.i16 rd, o(rs1) entero 16 bits, desplazamiento de registro EXT64 72 000 01 st.i16 rd, [rs1 + rs2] entero 16 bits, registro indexado REG32 72 001 01 st.i16 rd, sym32 + o entero 16 bits, símbolo + desplazamiento IMM64 72 010 01 st.i16 rd, sym32(rs1) entero 16 bits, símbolo + registro REG64 72 011 01 st.i16 rd, sym32 + o(rs1) entero 16 bits, símbolo + desplazamiento + registro REG96 72 101 10 st.iw rd, o(rs1) entero 32 bits (palabra), desplazamiento de registro IMM32 15 st.iw rd, [rs1 + rs2] entero 32 bits (palabra), registro indexado REG32 17 st.iw rd, sym32 + o entero 32 bits (palabra), símbolo + desplazamiento IMM64 25 st.iw rd, sym32(rs1) entero 32 bits (palabra), símbolo + registro REG64 76 st.iw rd, sym32 + o(rs1) entero 32 bits (palabra), símbolo + desplazamiento + registro REG96 72 100 01 st.u16 rd, o(rs1) entero sin signo 16 bits, desplazamiento de registro EXT64 72 000 11 st.u16 rd, [rs1 + rs2] entero sin signo 16 bits, registro indexado REG32 72 001 11 st.u16 rd, sym32 + o entero sin signo 16 bits, símbolo + desplazamiento IMM64 72 010 11 st.u16 rd, sym32(rs1) entero sin signo 16 bits, símbolo + registro REG64 72 011 11 st.u16 rd, sym32 + o(rs1) entero sin signo 16 bits, símbolo + desplazamiento + registro REG96 72 110 10 st.f fd, o(rs1) flotante, desplazamiento de registro EXT64 70 011 00 st.f fd, [rs1 + rs2] flotante, registro indexado REG32 70 011 10 st.f fd, sym32 + o flotante, símbolo + desplazamiento IMM64 70 010 00 st.f fd, sym32(rs1) flotante, símbolo + registro REG64 70 010 10 st.f fd, sym32 + o(rs1) flotante, símbolo + desplazamiento + registro REG96 72 110 11 st.d fd, o(rs1) doble, desplazamiento de registro EXT64 70 011 01 st.d fd, [rs1 + rs2] doble, registro indexado REG32 70 011 11 st.d fd, sym32 + o doble, símbolo + desplazamiento IMM64 70 010 01 st.d fd, sym32(rs1) doble, símbolo + registro REG64 70 010 11 st.d fd, sym32 + o(rs1) doble, símbolo + desplazamiento + registro REG96 72 111 00

Otras instrucciones Cargar valor constante38

Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. cnst rd, imm32 entero, inmediato REG64 53 cnst rd, sym32 + o entero, símbolo + desplazamiento IMM64 56

Insertar bits Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. bfins.i o(rs1){c1:c2}, rd entero, desplazamiento de registro BF64 73 001 00 bfins.i rs1{c1:c2}, rd entero, directo BF64 73 001 01 bfins.u o(rs1){c1:c2}, rd entero sin signo, desplazamiento de registro BF64 73 001 10 bfins.u rs1{c1:c2}, rd entero sin signo, directo BF64 73 001 11

Extraer bits Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. bfext.i rd, o(rs1){c1:c2} entero, desplazamiento de registro BF64 73 000 00 bfext.i rd, rs1{c1:c2} entero, directo BF64 73 000 01 bfext.u rd, o(rs1){c1:c2} entero sin signo, desplazamiento de registro BF64 73 000 10 bfext.u rd, rs1{c1:c2} entero sin signo, directo BF64 73 000 11

Mover Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. mov.i rd, rs1 entero, directo REG32 10 mov.i rd, imm16 entero, inmediato IMM32 23 mov.f fd, fimm flotante, inmediato REG64 70 100 11 mov.d fd, fimm doble, inmediato DOUBLE96 70 101 00

Mover y bifurcar (?) Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. movb rd, rs1, len entero, directo REG64 70 100 10

Mover desde memoria extendida (?) Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. movx rd, rs1 entero, indirecto (?) REG32 70 000 11

38 “La instrucción cnst toma un valor de índice y coloca la dirección del símbolo asociado en un registro.” [WAH97]

Page 24: ¿Es .NET una tecnología nueva?

¿Es .NET una tecnología nueva? Daniel Gayo Avello

24

Redondear a entero (?) Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. movfti.f rd, fs1 flotante, directo REG32 71 011 10 movfti.d rd, fs1 doble, directo REG32 71 011 11

Redondear a flotante (?) Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. movitf.f fd, rs1 entero, directo REG32 71 100 00

Redondear a doble (?) Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. movitf.d fd, rs1 entero, directo REG32 71 100 01

Convertir doble a entero Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. cvtd.i rd, fs1 entero/doble, directo REG32 71 100 10

Convertir doble a flotante Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. cvtd.f fd, fs1 flotante/doble, directo REG32 71 100 11

Convertir flotante a doble Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. cvt f .d fd, fs1 doble/flotante, directo REG32 71 101 00

Convertir flotante a entero Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. cvtf. i rd, fs1 entero/flotante, directo REG32 71 101 01

Convertir entero a flotante Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. cvti.f fd, rs1 flotante/entero, directo REG32 71 101 10

Convertir entero a doble Instrucción Operandos Tipo de los operandos y modo de direccionamiento Formato Op. Subop. cvti.d fd, rs1 doble/entero, directo REG32 71 101 11

Comentarios al juego de instrucciones

Las siguientes instrucciones emplean códigos de operación repetidos o carecen de subcódigo de operación, subopcode:

1. st.iw rd, o(rs1): opcode 15 2. ld.iw rd, [rs1+rs2]: comparte con la anterior el opcode. 3. call sym26: opcode 30. 4. ld.i8 rd, [rs1+rs2]: comparte con la anterior el opcode. 5. div.i rd, rs1, imm16: opcode 70 sin subopcode. 6. div.i rd, rs1, rs2: opcode 70 sin subopcode.

Los siguientes códigos y subcódigos de operación están “desocupados”:

• 16 • 34 • 70, 000 00 • 70, 000 01 • 70, 000 10 • 70, 000 11

Las hipótesis que se plantean son las siguientes:

• La instrucción [1] o la [2] tiene en realidad opcode 16. • La instrucción [3] o la [4] tiene en realidad opcode 34. • Las instrucciones [5] y [6] tienen opcode 70 y dos subopcodes de entre 00000, 00001, 00010 y 00011. • Las instrucciones enter y exit39 tienen opcode 70 y los dos subopcodes restantes. • El número total de instrucciones de OmniVM es de 169 (167 + enter + exit).

39 Las instrucciones mencionadas no aparecen en el repertorio de instrucciones de [WAH95] pero sí en el código Omniware que se muestra en [ERN97], ver la nota al pie [36].