Bienvenidos a nuestro blog, ¡Disfrútenlo!

lunes, 9 de julio de 2012


                        LIGADORES

   Es un programa que enlaza todos los programas o módulos obteniendo lo que denominamos programa ejecutable.
Es un programa que enlaza distintos módulos o programas que poseen subprogramas. Además incorporan las denominadas rutinas de librerías en caso de solicitarlas el propio programa.
La generación de un módulo ejecutable a partir de una colección de procedimientos traducidos independientemente requiere un ligador.

                                   FUNCIÓN

   Los editores de ligado pueden efectuar varias funciones últimas a demás de la simple preparación de un programa objeto para su ejecución estos también se pueden utilizar para construir paquetes de subrutinas u otras secciones que suelen utilizar juntas. Esto puede ser útil al tratar con bibliotecas de subrutinas que manejan lenguajes de programación de alto nivel. Comparados con los cargadores de ligadores los editores de ligado en general tienden a ofrecer mayor flexibilidad y control con el correspondiente incremento e complejidad y sobrecarga.
   La tarea principal del enlazador es resolver referencias externas lleva a cabo la siguiente etapa del proceso de traducción enlazando los módulos ensambladores y los acervos para formar un programa completo. En algunos sistemas el cargador simplemente copia el programa ejecutable a las posiciones de memorias apropiadas.

                            Sus principales funciones son:

 Enlazar código intermedio compilado independientemente en un solo módulo de carga resolviendo las diferencias entre Tokens.
 Incorpora las denominadas rutinas de librerías en caso de solicitarlas el propio programa.
 Su función es reducir procedimientos traducidos por separado y enlazarlos para que se ejecuten como una unidad llamada programa binario ejecutable.



                         TIPOS EDITORES DE LIGADO.

   La diferencia fundamental entre un editor de ligado y un cargador ligador es: Primero se ensambla o compila el programa fuente, produciendo un programa objeto (que puede contener varias secciones de control diferentes).

   Un cargador ligador realiza todas las operaciones de ligado y relocalización, incluyendo búsqueda automática en bibliotecas, si se especifica, y carga el programa ligado directamente en la memoria para su ejecución. Por otro lado, un editor de ligado produce una versión ligada del programa (llamada a menudo módulo de carga o imagen ejecutable), que se escribe un archivo o biblioteca para su ejecución posterior. Cuando el usuario esta listo para ejecutar el programa ligado, se puede utilizar un cargador re localizador simple para cargar el programa en la memoria. El editor de ligado realiza relocalización de todas las secciones de control relativas al inicio del programa ligado.

                         LIGADOR DINAMICO.

    El ligador dinámico ofrece algunas ventajas sobre los otros tipos de ligado. Proporciona la posibilidad de cargar las rutinas sólo cuando y si se necesitan. Si las subrutinas son grandes o tienen muchas referencias externas, se pueden conseguir ahorros considerables de tiempo y espacio de memoria.
Cuando se utiliza el ligador dinámico, la asociación de una dirección real y el nombre simbólico de la rutina llamada no se hace hasta que se ejecuta la proposición llamada.

                         LIGADOR DEL SISTEMA VAX.

    El ligador VAX es un editor de ligado que realiza las mismas funciones básicas alcanzadas con anterioridad. La acción del ligador en la creación de las secciones de imagen está controlada por ensamblador o compilador por medio de una secuencia de mandatos que forman parte del programa objeto.
El lenguaje de mandatos ofrece una gran diversidad de posibilidades: hay más de 50 códigos de mandatos posibles. El ligador VAX puede generar tres tipos de imágenes. Una imagen ejecutable es aquella adecuada para la carga y ejecución; sin embargo, el ligador no puede reprocesar este tipo de imagen.

     Una imagen compartible no es ejecutable, pero el ligador puede reprocesarla, y se puede utilizar, por ejemplo como tapa intermedia en el ligado de un programa muy grande. Las imágenes compartibles también hacen posibles que diferentes
Programas compartan la misma copia de ciertas instrucciones o área de datos. El tercer tipo de imagen que puede crear el ligado es una imagen de sistema, concebida para ser ejecutada directamente en la máquina VAX.

                        EDITOR DE LIGADO DEL SISTEMA /370.

    El formato de los programas objeto manejado por el editor de ligado del SISTEMA /370 es muy parecido al analizado para SIC/XE. La técnica de referencia a un número se usa para mejorar la eficiencia. El programa de salida del editor de ligado se llama módulo de carga, y puede cargarse en la memoria para su ejecución, y suele contener suficiente información para permitir que el editor de ligado los reprocese.

                         LIGADORES ESTATICOS.

   Cuando se utilizan subrutinas en un programa, el código ejecutable de cada una de ellas debe encontrarse en memoria al tiempo de ejecución. Para esto, antes de cargar un programa, debe ligarse su código objeto con los códigos objeto (guardados en uno o más archivos) de cada una de las subrutinas invocadas por él, obteniendo así un programa ejecutable que contiene tanto el código del módulo invocador como el código de los módulos invocados. En este punto, es posible guardar el resultado del proceso de liga en un archivo que podrá ser utilizado por un cargador, o el mismo programa ligador puede también realizar la tarea de carga. Esto último evita el tener que guardar el código ejecutable en un archivo, con lo que se ahorra espacio en disco. Este ahorro de espacio en disco se paga con el tiempo gastado al tener que ligar todos los módulos cada vez que se necesite ejecutar el programa.


                           LIGADURAS DE SUBRUTINAS

Al estudiar subrutinas es conveniente identificar dos funciones distintas: el invocador y el invocado.
El invocador: es la sección de código que inicia la llamada a la subrutina.
El invocado: es la subrutina llamada por el invocador.
Examinaremos las transferencias de control necesarias para implantar la invocación de subrutinas. Al efectuar una llamada se transfiere el control al cuerpo de la subrutina; cuando esta concluye, devuelve el control a la instrucción que está inmediatamente después de la llamada a la subrutina desde diversos puntos en el programa, las subrutinas deben ser capaces de regresar a distintos lugares del programa. La dirección de retorno es la dirección de la instrucción que esta inmediatamente después de la instrucción que transfiere el control al invocado. El invocador proporciona la dirección del retorno al invocado como parte de enlace de la subrutina.
El enlace de subrutina es la estructura con que se comparte información sobre el invocador y el invocado. El invocador establece parte del enlace de subrutina en una secuencia de instrucciones llamada secuencia de arranque. El invocado establece lo que resta del enlace de subrutina en el prologo de subrutina, el cuál puede estar vacío como suponemos de todas las rutinas están bien estructuradas, las instrucciones al final de la subrutina regresan al invocador. Está secuencia de instrucciones se denomina epílogo de subrutina. Cuando el control regresa al invocador, es posible que este tenga que limpiar el enlace; este conjunto de instrucciones se denomina secuencia de limpieza y también puede estar vacía.

                                   LIGADOR  EN C++


Si algún archivo fuente hace referencia a funciones de una biblioteca o de funciones que están definidas en otros archivos fuentes, el ligador combina estas funciones (con main ()) para crear un archivo ejecutable. Las referencias a variables externas en esta etapa son resueltas.

                             USO DE LAS BIBLIOTECAS


C es un lenguaje extremadamente pequeño. Muchas de las funciones que tienen otros lenguajes no están en C, por ejemplo, no hay funciones para E/S, manejo de cadenas o funciones matemáticas.

La funcionalidad de C se obtiene a través de un rico conjunto de bibliotecas de funciones.

Como resultado, muchas implementaciones de C incluyen bibliotecas estándar de funciones para varias finalidades. Para muchos propósitos básicos estas podrían ser consideradas como parte de C. Pero pueden variar de máquina a máquina.

Un programador puede también desarrollar sus propias funciones de biblioteca e incluso bibliotecas especiales de terceros, por ejemplo, NAG o PHIGS.

Todas las bibliotecas (excepto E/S estándar) requieren ser explícitamente ligadas con la opción -l y, posiblemente con L, como se señalo previamente.

Procesamiento de un programa objeto por medio de (a) cargador ligador y (b) editor de ligado.

CREACIÓN DE UNA BIBLIOTECA ESTÁTICA

Si se tiene un conjunto de rutinas que se usen en forma frecuente, se podría desear agruparlas en un conjunto de archivos fuente, compilar cada archivo fuente en un archivo objeto, y entonces crear una biblioteca con los archivos objeto.
Supongamos que se tiene un conjunto de archivos que contengan rutinas que son usadas frecuentemente, por ejemplo un archivo cubo.c:
float cubo(float x)
{
return (x*x*x);
}
y otro archivo factorial.c
int factorial(int n)
{
int i, res=1;
for(i=1; i<=n; i++)
res*=i;
return (res);
}
Para los archivos de nuestras funciones también se debe tener un archivo de cabecera, para que puedan ser usadas, suponiendo que se tiene el siguiente archivo libmm.h con el siguiente contenido:

externfloat cubo (float);
externint factorial (int);




El código que use la biblioteca que se esta creando podría ser:
/* Programa prueba.c */
#include "libmm.h"
#define VALOR 4
main()
{
printf("El cubo de %d es %fn",VALOR, cubo(VALOR) );
printf("t y su factorial es %dn",factorial(VALOR) );
}
Para crear la biblioteca se deben compilar los archivos fuente, que lo podemos hacer de la siguiente forma:
$ gcc -c cubo.c factorial.c
Lo cual nos dejará los archivos cubo.o y factorial. o. Después se debe crear la biblioteca con los archivos fuentes, suponiendo que nuestra biblioteca se llame libmm.a, tendrás que hacerlo con el comando ar así:

$ ar r libmm.a cubo.o factorial. O
Cuando se actualiza una biblioteca, se necesita borrar el archivo anterior (libmm.a). El último paso es crear un índice para la biblioteca, lo que permite que el ligador pueda encontrar las rutinas. Lo anterior, lo hacemos con el comando ranlib, por lo que teclearemos ahora:
$ ranlib libmm.a
Los últimos dos pasos pudieron ser combinados en uno sólo, entonces hubiéramos podido teclear:
$ar rs libmm.a cubo.o factorial. O


Ahora que ya tenemos la biblioteca, es conveniente que coloquemos nuestra biblioteca y el archivo cabecera en algún lugar apropiado. Supongamos que dejamos la biblioteca en ~/lib y el fichero cabecera en ~/include, debemos hacer lo siguiente:
$ mkdir../include
$ mkdir ../lib
$ mv libmm.h ../include
$ mv libmm.a ../lib
Si llegarás a modificar la biblioteca, tendrías que repetir la última instrucción.
Se debe ahora compilar el archivo con la biblioteca, de la siguiente forma:
gcc -I../include -L../lib -o prueba prueba.c –lmm

             CREACIÓN DE UNA BIBLIOTECA COMPARTIDA

Las ventajas que presentan las bibliotecas compartidas, es la reducción en el consumo de memoria, si son usadas por más de un proceso, además de la reducción del tamaño del código ejecutable. También se hace el desarrollo más fácil, ya que cuando se hace algún cambio en la biblioteca, no se necesita recompilar y reenlazar la aplicación cada vez. Se requiere lo anterior sólo si se modifico el número de argumentos con los que se llama una función o se cambio el tamaño de alguna estructura.
El código de la biblioteca compartida necesita ser independiente de la posición, para hacer posible que sea usado el código por varios programas. Para crear la biblioteca hacerlo de la siguiente forma:
$ gcc -c -fPIC cubo.c factorial.c
Para generar la biblioteca dinámica hacer lo siguiente:
$ gcc -shared -o libmm.so cubo.o factorial. O

No existe un paso para la indexación como ocurre en las bibliotecas estáticas.
Después habrá que mover la biblioteca dinámica a su directorio correspondiente (../lib) y proceder a compilar para que nuestro código use la biblioteca.

$ gcc -I../include -L../lib -o prueba prueba.c –lmm
Nos preguntamos que sucede si hay una biblioteca compartida (libmm.so) y una estática (libmm.a) disponibles. En este caso, el ligador siempre toma la compartida. Si se desea hacer uso de la estática, se tendrá que nombrar explícitamente en la línea de comandos:
$ gcc -I../include -L../lib -o prueba prueba.c libmm.a
Cuando se usan bibliotecas compartidas un comando útil es ldd, el cual nos informa que bibliotecas compartidas un programa ejecutable usa, a continuación un ejemplo:
Como se ve en cada línea aparece el nombre de la biblioteca, el camino completo a la biblioteca que es usada, y donde en el espacio de direcciones virtuales la biblioteca esta mapeada.

            EJEMPLOS

    Escribe, compila y corre el siguiente programa. Modifica el programa para que incluyas tu nombre y la forma como hiciste lo anterior. Para iniciar un comentario usa /* y para terminarlo */:
main()
{
int i;


printf("t Numero ttCubonn");


for( i=0; i<=20; ++i)

printf("t %d ttt %d n",i,i*i*i );

}
    El siguiente programa usa la biblioteca matemática. Tecléalo, compílalo y ejecútalo. Incluye con comentarios dentro del programa tu nombre y la forma como hiciste lo anterior.
#include <math.h>

main()
{
int i;

printf("tAngulottSenonn");


for( i=0; i<=360; i+=15)

printf("t %d ttt %f n",i,sin((double) i*M_PI/180.0));

}

            TABLA Y LOGICAS DE UN CARGADOR LIGADOR

El algoritmo de un cargador ligador es mucho más complicado que el del cargador absoluto. La entrada de este cargador consta de un conjunto de programas objeto que se van a ligar. Una sección de control puede hacer una referencia externa a un símbolo cuya definición aparece mas adelante en este flujo de entrada. En ese caso, la operación de ligado requerida no se puede realizar hasta haber asignado una dirección al símbolo externo implicado. Así pues, un cargador ligador suele dar dos pasos sobre su entrada, al igual que hace un ensamblador. El paso 1 asigna direcciones a todos los símbolos externos, y el paso 2 realiza la carga, la relocalización y el ligado.      
La principal estructura de datos necesaria para el cargador ligador es una tabla de símbolos externos TABSE. Esta tabla, análoga a TABSIM del algoritmo del ensamblador, se usa par almacenar el nombre y la dirección de los símbolos externos en el conjunto de secciones de control que se está cargando. Otras dos variables importantes son DIRPROG (dirección de carga del programa) y DIRSC (dirección de la sección de control). DIRPROG es la dirección inicial de la memoria donde se va a cargar el programa ligado, DIRSC contiene la dirección inicial asignada a la sección de control que está examinando el cargador.  

                                                                                                                                                                                                                                                                                                                                                                                                    

            EJEMPLO DE LIGADOR EN TURBO C.

Captura los siguientes archivos en C y compílalos con el compilador de Turbo C para generar el código en ensamblador y el código objeto de cada uno de ellos.

/* Archivo: a.c
* Ilustra el uso de programas multimodulos */
#include<io.h>
#include<string.h>
void a(char *s)
{
int i;
for(i=0;i<10;i++)
write(1,s,strlen(s));
}
------------------------ +++ ---------------------------
/* Archivo: m.c
* Ilustra el desarrollo de programas multimodulos */
externvoid a(char *);
intmain()
{
staticchar texto[]="Hola Mundo desde programa multimodulo!\n";
a(texto);
return 0;
}