¿Qué es un vinculador?

Esto es parte de una serie más grande conocida como “Cómo Programar Cualquier Cosa: Core Rulebook

Prefacio

Con lenguajes de bajo nivel y de nivel medio como el ensamblador y C, es probable que se encuentre preocupado por “el vinculador” en algún momento, probablemente cuando hay un error y no se puede encontrar un símbolo. ¿Qué es este vinculador misterioso, y qué hace? Para los lenguajes de nivel superior, en particular los idiomas interpretados como Python probablemente no encontrará esto tanto como si no estuviera compilando extensiones de origen como pygame. Sin embargo, en PHP existe una especie de término medio, ya que es posible cargar dinámicamente o enlazar bibliotecas en el entorno de tiempo de ejecución de PHP durante la ejecución del programa con el comando dl (). Sólo me enteré de la vinculación y lo que hace, y cómo lo hace cuando empecé a estudiar el montaje en una máquina Linux a mí mismo.

¿Dónde utilizamos un vinculador?

Para entender el papel del enlazador debemos revisar nuestro proceso de compilación. Tomo prestado el diagrama del proceso de compilación del artículo “Interpretación versus compilación” a continuación:

Aquí puede ver el paso “Enlaces juntos en ejecutable”. Ahí es donde entra el enlazador. El compilador produce lo que se conoce como archivos de código de objeto. Estos archivos no son exactamente código de máquina lista para ejecutable. Son piezas de un programa con etiquetas e información sobre qué símbolos corresponden a qué código de máquina. El enlazador, que no se representa exactamente en el diagrama anterior, toma estos archivos de código objeto y “pega” juntos todos los símbolos de modo que dondequiera que se encuentren en los archivos de objeto que se resuelven a su correspondiente código de máquina. Voy a explicar más, pero primero, a continuación se muestra un diagrama más detallado del proceso de vinculación:

Cuando se escribe un programa, como un programa C, a menudo se escriben partes o módulos del programa en archivos separados. Estos archivos definirán funciones, variables globales, macros, etc. (para obtener más cobertura sobre qué función o variable global se puede ver en mi mensaje de Programming Crash Course). Cuando envíe estos archivos de programa al compilador, generará el código de máquina apropiado para cada definición en cada archivo, y para cada archivo normalmente, generará un archivo que corresponda a cada definición (conocida como símbolo en este punto) a la Código de máquina generado. Sin embargo, el programa no es ejecutable en este momento.

Imagine que tiene dos archivos de programa, FileOne y FileTwo. Cada uno define sus propias funciones como a continuación:

Ahora, cuando el compilador compila cada uno de estos archivos crea código de objeto. Cada archivo de código de objeto tiene una lista de símbolos, para FileOne que es main y myFunction, para FileTwo que es anotherFunction y el código de máquina correspondiente que ejecutará el procesador. Sin embargo, note algo: FileOne llama a anotherFunction () dentro de myFunction () … eso significa que en el archivo de objeto generado por el compilador para FileOne hay una pieza que falta. En ninguna parte del código objeto de FileOne es anotherFunction y su correspondiente código de máquina definido. ¿Cómo va el procesador a saber qué hacer?

Ahí es donde entra el enlazador. Lleva ambos archivos e identifica todos los símbolos: main, myFunction y anotherFunction. A continuación, coloca todo el código de máquina correspondiente en, presumiblemente, un archivo donde llena la llamada a anotherFunction en myFunction con la dirección de memoria adecuada en el ejecutable. Puede hacerlo porque conoce todos los símbolos de todos los archivos y su correspondiente código de máquina.

¿Por qué usamos un vinculador?

Hay muchas razones para usar un enlazador; La mayoría de ellos tienen que ver con la organización del código del programa. La primera razón es que podemos romper el código del programa, el código que escribimos, en archivos separados. Esto permite al programador no sólo organizar el código de una manera fácil de usar, uniendo funciones asociadas formando lo que comúnmente se conoce como módulos, sino que también permite a varios programadores trabajar juntos más fácilmente. Si las diferentes funcionalidades del programa se escriben en diferentes módulos / archivos, entonces un programador puede trabajar en un archivo sin tener que molestar a otros archivos, mientras que si el programa estuviera en un archivo gigante tendría varias personas editando un archivo a la vez y eso sería horrible. Cada programador en este escenario tendría que esperar hasta que el programador anterior haya terminado de editar el archivo. Usted puede ver cómo eso sería contraproducente. Además, los programas de control de versiones de origen como Git o Subversion (que se cubrirán en otro lugar) aprovechan el enfoque de archivos múltiples para realizar un seguimiento de los cambios.

Otra razón para usar un enlazador es que, con un enlazador, podemos utilizar lo que comúnmente se conoce como bibliotecas. Una biblioteca de programas es un montón de código que expone algún tipo de interfaz (normalmente un montón de funciones y puntos de entrada a código de máquina, o si está orientado a objetos, que se tratará en Bootstrap Parte 2 a partir de esta escritura, clases) Un montón de código de máquina que hace algo. Una interfaz en este sentido es un montón de símbolos que algún otro programa puede hacer referencia para obtener esa funcionalidad adicional. Esto es más evidente cuando estamos programando en C y usamos la Biblioteca C Estándar. C en sí como un lenguaje especificado no define una gran cantidad de funcionalidad, como los operadores de entrada / salida, como con muchos otros idiomas, y en su lugar, pone ese tipo de funcionalidad en una biblioteca estándar que está vinculado. Cuando el programa C se compila y enlaza, el enlazador busca los símbolos de biblioteca en el código de objeto de la biblioteca y llena los espacios en blanco. Esto es cierto para cualquier biblioteca, como por ejemplo una que tenga que ocuparse de gráficos por ejemplo.

Nota: Lo que hemos descrito hasta ahora se conoce como enlace estático, donde el ejecutable final incluye todo el código de máquina necesario de todas las fuentes para ejecutarse de forma independiente. Algunos sistemas operativos y enlazadores tienen la opción de realizar vinculación dinámica, donde una biblioteca dinámica se carga en la memoria como el programa se ejecuta y, a continuación, vinculados en tiempo real contra el programa existente. Estos programas tienen así símbolos desconocidos dentro de ellos incluso antes de que se ejecuten.

Hay un pro a la vinculación dinámica y que es que sólo realmente tiene que tener la biblioteca dinámica almacenada una vez en algún lugar en las entrañas de la computadora, mientras que múltiples ejecutables enlace a él cuando se ejecutan. Sin embargo, existe un inconveniente en este método, y es que si la biblioteca dinámica se actualiza o reescribe de forma no compatible con versiones anteriores y se reemplaza en el sistema, todos los programas anteriores se romperán y no se ejecutarán. Esto no ocurriría si estuvieran vinculados estáticamente y tuvieran todo su código de máquina independientemente.

Otra función, más esotérica, de un enlazador es lo que se conoce como reubicación. Normalmente un archivo de objeto define todos los símbolos que pueden ser utilizados por otro archivo de objeto externo a él, sin embargo, también define símbolos para uso interno. Estos símbolos pueden ser traducidos y filtrados por el enlazador en un proceso conocido como reubicación. El proceso es algo como esto: el compilador no tiene medios de conocer el diseño final del código de la máquina en la salida final, por lo que no puede aprovechar las instrucciones que pueden ser más eficientes dado un cierto diseño del código. Por lo tanto, el compilador genera la instrucción más conservadora, que puede ser la más lenta o ineficiente en el diseño final del código de máquina, pero añade lo que se conoce como consejos de relajación. Una vez que todos los objetos de entrada se han leído y asignado direcciones temporales, el enlazador puede realizar lo que se conoce como un paso de relajación que reasigna las direcciones para que las instrucciones más eficientes pueden ser sustituidos por las conjeturas conservadoras que hizo el compilador. Esto, a su vez, puede permitir relajaciones adicionales, y así sucesivamente. En general, un programador medio que no implementa su propio enlazador o compilador no puede considerar estas cuestiones con seguridad; El enlazador se encargará de ello.

Conclusión

Los enlazadores son piezas esenciales del proceso de desarrollo de software y permiten a los programadores separar su código de maneras significativas. También permiten el uso de bibliotecas de programación, código que se ha generado para ser utilizado por otros programas. La Biblioteca Estándar C es un ejemplo de tal biblioteca, permitiendo que los programas estándar de C se enlacen a su código para realizar operaciones tales como entradas / salidas generales. Si no tuviéramos un enlazador en nuestro proceso de compilación tendríamos que escribir todos nuestros programas como un archivo monolítico gigante. Si varios programadores estaban trabajando en un programa cada uno tendría que esperar a que el otro se hiciera antes de perseguir sus propios objetivos en el mismo archivo. Esto es innecesario con un enlazador que puede enlazar varios archivos en un solo ejecutable. Espero que este artículo aclarado lo que un vinculador hace por usted, pero si usted tiene alguna pregunta no dude en dejarlos en los comentarios. ¡Gracias por leer!

Esto es parte de una serie más grande conocida como “Cómo Programar Cualquier Cosa: Core Rulebook

Si usted aprecia este artículo usted puede ser que considere el apoyar de mi Patreon.

Pero si un compromiso mensual es un poco más, lo entiendo, podría considerar comprarme un café.

photo credit: xmodulo Shared Library Info via photopin (license)

También te podría gustar...

Deja un comentario

A %d blogueros les gusta esto: