El proceso de compilación de un programa en Go, como un simple 'Hola Mundo', es más complejo de lo que parece inicialmente. El compilador transforma el código fuente en archivos objeto (.o) que contienen código de máquina, definiciones de símbolos y marcadores de posición para direcciones que necesitan ser ajustadas. Sin embargo, un programa real rara vez se compone de un solo archivo objeto; típicamente importa funciones y estructuras de datos de múltiples paquetes, cada uno compilado por separado. Aquí es donde entra en juego el enlazador (linker).
El enlazador tiene cuatro tareas principales: Resolución de Símbolos, Relocalización, Eliminación de Código Muerto y Generación del Ejecutable. La Resolución de Símbolos implica conectar referencias a funciones y variables definidas en otros archivos objeto. La Relocalización corrige los marcadores de posición de direcciones en el código de máquina con las direcciones reales una vez que se conoce la ubicación de cada componente en la memoria. La Eliminación de Código Muerto elimina funciones y datos no utilizados, optimizando el tamaño del ejecutable. Finalmente, la Generación del Ejecutable determina la disposición de código y datos en la memoria y crea un archivo ejecutable en un formato específico del sistema operativo (ELF en Linux, Mach-O en macOS, PE en Windows).
El enlazador se apoya en el 'Loader' para construir un índice global de símbolos. El Loader recorre recursivamente las dependencias de un programa, cargando archivos objeto y registrando símbolos y sus referencias. Luego, el enlazador elimina el código no utilizado mediante un rastreo de referencias desde el punto de entrada principal. Finalmente, asigna direcciones a los símbolos y corrige los marcadores de posición en el código de máquina en dos pasadas: asignación de direcciones y parcheo de relocaciones. Este proceso asegura que todas las referencias a símbolos externos se resuelvan correctamente y que el ejecutable resultante sea eficiente y funcional.
En resumen, el enlazador es un componente crucial en el proceso de compilación de Go, que transforma múltiples archivos objeto en un único ejecutable funcional, optimizando el tamaño y resolviendo dependencias entre paquetes.
