Diferencias
Muestra las diferencias entre dos versiones de la página.
| Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previa | ||
| td3:guiasupervivenciald [2016/04/11 21:35] – Se incorpora el enlace C y ASM cnigri | td3:guiasupervivenciald [2026/09/18 21:36] (actual) – borrado - editor externo (Fecha desconocida) 127.0.0.1 | ||
|---|---|---|---|
| Línea 1: | Línea 1: | ||
| - | ======= Guía de supervivencia del enlazador ======= | ||
| - | ====== Descargo ====== | ||
| - | \\ Esta guía no pretende ser un texto que trate de forma exhaustiva los pormenores de los métodos de enlace, su asociación con la interfaz binaria de la aplicación específica de una arquitectura [[http:// | ||
| - | ====== Introducción ====== | ||
| - | La guía solo cubre la sintaxis GNU LD, la ABI x86-64 desarrollada por el Tool Interface Standards (TIS) del UNIX System Laboratories. | ||
| - | Para una mejor comprensión de los conceptos que se brindarán, el lector debe estar familiarizado con los siguiente temas: | ||
| - | * Etapas de la generación de un binario ejecutable (ensamblar, compilar, preprocesar, | ||
| - | * Executable and Linking Format (ELF) | ||
| - | * Manejo básico del lenguaje ensamblador. | ||
| - | |||
| - | ==== Misión de un enlazador ==== | ||
| - | El enlazador tiene por objetivo combinar varios fragmentos de código y datos en un único fichero binario, el cual puede presentar la misma organización de la memoria en la cual se carga, por ejemplo una ROM, o diferir virtualmente como es el caso de la RAM. Vale la pena aclarar que también es posible que la organización de la memoria de carga (ROM) coincida con la virtual (RAM). | ||
| - | |||
| - | Lo expresado anteriormente puede comprenderse claramente mediante el siguiente ejemplo. | ||
| - | <code c> | ||
| - | Se desea realizar un programa que modifique un área de 4MB de memoria a ' | ||
| - | en cualquier valor, sabiendo que el sistema solo cuenta con una ROM de 1kB y una RAM de 8MB. | ||
| - | Antes de continuar leyendo reflexione sobre lo siguiente: | ||
| - | * Elabore mentalmente un programa que cumpla con el objetivo propuesto, sabiendo que el sistema no dispone de ningún sistema operativo. | ||
| - | * ¿Donde se encuentra el programa durante la ejecución? | ||
| - | * ¿Como llegó a la ubicación de la respuesta anterior? | ||
| - | * ¿Que tamaño, aunque más no sea basandose en el tamaño de los datos, estima que tendría el programa? | ||
| - | </ | ||
| - | {{ td3: | ||
| - | Si la propuesta es: | ||
| - | <code c> | ||
| - | #define MEM_4MB | ||
| - | char mem_ptr_a[MEM_4MB]={' | ||
| - | |||
| - | int main(void) | ||
| - | { | ||
| - | | ||
| - | |||
| - | | ||
| - | { | ||
| - | mem_ptr_a[contador]=0; | ||
| - | } | ||
| - | } </ | ||
| - | |||
| - | Resulta evidente que el binario generado ocupará al menos 4MBytes, por lo cual no cabrá en la ROM del sistema. | ||
| - | Para solventar el problema mencionado se propone: | ||
| - | <code c> | ||
| - | char mem_ptr_c[]=" | ||
| - | |||
| - | int main(void) | ||
| - | { | ||
| - | | ||
| - | |||
| - | | ||
| - | { | ||
| - | mem_ptr_c[contador]=0; | ||
| - | } | ||
| - | } </ | ||
| - | |||
| - | El código propuesto genera un binario con el siguiente mapa de memoria simplificado. | ||
| - | ^ | ||
| - | | 0x0000 | ||
| - | ^ CODIGO ^ 0x0034\\ 0x005D ^ | ||
| - | | || | ||
| - | ^ DATOS ^ 0x0060\\ 0x006A ^ | ||
| - | | 0x0400 | ||
| - | |||
| - | A partir del mismo se aprecia que no es posible utilizar este binario como código de arranque de nuestro sistema ya que la familia de procesadores Intel IA32 requieren que el mismo se encuentre a partir de la dirección 0xFFFF0, es decir, que el mapa de memoria necesario seria | ||
| - | |||
| - | ^ | ||
| - | | 0xFFBFF | ||
| - | ^ DATOS ^ DAT_INI\\ DAT_FIN ^ | ||
| - | | || | ||
| - | ^ CODIGO | ||
| - | | 0xFFFF0 | ||
| - | |||
| - | Como puede observarse no solo es necesario alterar el orden de la sección de datos y código sino que también, se deben modificar las direcciones de los punteros de datos y las llamadas a función. | ||
| - | |||
| - | Para resolver esta clase de problemas se utiliza el enlazador. Sin más preámbulos, | ||
| - | |||
| - | ====== Utilización de LD ====== | ||
| - | ===== Conceptos básicos ===== | ||
| - | Al igual que muchas otras herramientas **GNU**, conocidas en la jerga como // | ||
| - | <code text> | ||
| - | ld < | ||
| - | </ | ||
| - | Dentro de las múltiples opciones que presenta la herramienta, | ||
| - | Este //script// conocido como //linker script// o //lds// por su extensión permite indicarle al //**ld**// como debe organizar las diferentes secciones que compondrán el fichero de salida, así como también en que dirección deberán ser referenciadas y/o ubicadas físicamente las mismas. Estas direcciones, | ||
| - | El enlazador asocia cada sección de un programa a dos direcciones de memoria, que pueden coincidir o no: | ||
| - | * **Virtual [VMA] :** es aquella donde el binario copia los datos modificables y en algunos casos una parte de si mismo para su posterior ejecución, por lo general se la asocia a la memoria RAM. | ||
| - | * **De Carga [LMA] :** es aquella en donde se almacena el binario para su ejecución inicial, generalmente se la asocia a la memoria ROM. | ||
| - | |||
| - | ===== Formato de un LDS ===== | ||
| - | Un fichero //linker script// puede llegar a ser tan críptico que se requieran varios minutos en comprender su objetivo, sin embargo conceptualmente se debe comprender que no es más que un agrupamiento de secciones y sus respectivas direcciones VMA y LMA, las cuales se gestionan mediante el contador de posición cuyo símbolo es el punto[**.**]. La función de este último es simplemente disponer de un índice que permite gestionar los desplazamientos relativos y/o absolutos de una sección respecto a otra. | ||
| - | Si bien existe una gran cantidad de opciones y comandos disponibles para un //linker script//, la presente guía se focalizará en el siguiente modelo, el cual se explicará con ejemplos en los apartados siguientes. | ||
| - | <code text> | ||
| - | SECTIONS | ||
| - | { | ||
| - | | ||
| - | | ||
| - | { | ||
| - | | ||
| - | } [> | ||
| - | } | ||
| - | </ | ||
| - | ===== Casos de uso ===== | ||
| - | ==== Reubicación completa del binario ==== | ||
| - | El siguiente ejemplo tiene por objetivo mostrar como es posible modificar las direcciones absolutas de las variables, es decir el equivalente a la directiva **//ORG//** de NASM. Como se puede apreciar las direcciones se desplazan a partir de la dirección 0x08000. | ||
| - | El fichero de enlace es // | ||
| - | <code text> | ||
| - | SECTIONS | ||
| - | { | ||
| - | . = 0x08000; | ||
| - | } | ||
| - | </ | ||
| - | Los parámetros del enlazador | ||
| - | <code text> | ||
| - | ld -z max-page-size=0x01000 --oformat=binary -m elf_i386 -T mi_linker_script.lds -e Inicio fichero.elf -o fichero.bin | ||
| - | </ | ||
| - | El código y sus salidas | ||
| - | <code asm> | ||
| - | +--------------------------------+--------------------------------------------------------------+--------------------+ | ||
| - | |.asm | .lst | ||
| - | +--------------------------------+--------------------------------------------------------------+--------------------+ | ||
| - | | 1 bits 32 | 1 bits 32 | ||
| - | | 2 | 2 | ||
| - | | 3 global Inicio | ||
| - | | 4 | 4 | ||
| - | | 5 dato: dw 0x1234 | ||
| - | | 6 | 6 | ||
| - | | 7 Inicio: | ||
| - | | 8 xor edi, edi | ||
| - | | 9 mov dword edi, 0x01 | 9 00000004 BF01000000 | ||
| - | |10 xor esi, esi | 10 00000009 31F6 xor esi, esi | 31 f6 | | ||
| - | |11 mov dword esi, | ||
| - | |12 mov dword [variable], 0x04 | 12 00000010 C705[2E000000]0400- | ||
| - | |13 | 13 00000018 0000 | 00 04 00 00 00 | | ||
| - | |14 xor eax, eax | 14 0000001A 31C0 xor eax, eax | 31 c0 | | ||
| - | |15 add eax, edi | 15 0000001C 01F8 add eax, edi | 01 f8 | | ||
| - | |16 add eax, esi | 16 0000001E 01F0 add eax, esi | 01 f0 | | ||
| - | |17 add dword eax, [variable] | ||
| - | |18 mov dword [resultado], | ||
| - | |19 | ||
| - | |20 | 20 | ||
| - | |21 | ||
| - | |22 | ||
| - | +--------------------------------+--------------------------------------------------------------+--------------------+ | ||
| - | </ | ||
| - | |||
| - | ==== Reubicación de secciones ==== | ||
| - | A continuación se muestra como es posible reubicar diferentes secciones de un programa. Como se puede apreciar las direcciones que referencian a los datos y de las bifuraciones de flujo de ejecución se modifican según lo especificado en el fichro " | ||
| - | El fichero de enlace es // | ||
| - | <code text> | ||
| - | SECTIONS | ||
| - | { | ||
| - | . = 0x08000; | ||
| - | | ||
| - | | ||
| - | |||
| - | . = 0x09000; | ||
| - | | ||
| - | | ||
| - | |||
| - | . = 0x0A000; | ||
| - | | ||
| - | | ||
| - | |||
| - | . = 0x0B000; | ||
| - | | ||
| - | } | ||
| - | </ | ||
| - | Los parámetros del enlazador | ||
| - | <code text> | ||
| - | ld -z max-page-size=0x01000 --oformat=binary -m elf_i386 -T mi_linker_script.lds -e Inicio fichero.elf -o fichero.bin | ||
| - | </ | ||
| - | El código y sus salidas | ||
| - | <code asm> | ||
| - | +-----------------------------------+--------------------------------------------------------------------------+--------------------+ | ||
| - | |.asm | .lst | ||
| - | +-----------------------------------+--------------------------------------------------------------------------+--------------------+ | ||
| - | | 1 bits 32 | ||
| - | | 2 | ||
| - | | 3 global Inicio | ||
| - | | 4 | ||
| - | | 5 SECTION .codigo_principal | ||
| - | | 6 | ||
| - | | 7 Inicio: | ||
| - | | 8 mov dword edi, 0x1 | 8 00000000 BF01000000 | ||
| - | | 9 mov dword [parametro_a], | ||
| - | |10 mov dword esi, 0x3 | 10 0000000B BE03000000 | ||
| - | |11 mov dword [parametro_b], | ||
| - | |12 call far [_suma] | ||
| - | |13 mov dword eax, [resultado] | ||
| - | |14 add dword eax, [variable_a] | ||
| - | |15 mov dword [array_a], eax | 15 00000027 A3[08000000] | ||
| - | |16 | ||
| - | |17 | 17 | ||
| - | |18 SECTION .dat_inic_a progbits | ||
| - | |19 | ||
| - | |20 | ||
| - | |21 | 21 | ||
| - | |22 SECTION .dat_no_inic_a nobits1 | ||
| - | |23 | ||
| - | | | ||
| - | |24 | ||
| - | |25 | ||
| - | |26 | ||
| - | |27 | 27 | ||
| - | |28 SECTION .codigo_funciones | ||
| - | |29 | ||
| - | |30 push edi | 30 00000000 57 push edi | 57 | | ||
| - | |31 push esi | 31 00000001 56 push esi | 56 | | ||
| - | |32 push eax | 32 00000002 50 push eax | 50 | | ||
| - | |33 | 33 | ||
| - | |34 xor edi, edi | 34 00000003 31FF xor edi, edi | 31 ff | | ||
| - | |35 mov dword edi, | ||
| - | |36 xor esi, esi | 36 0000000B 31F6 xor esi, esi | 31 f6 | | ||
| - | |37 mov dword esi, | ||
| - | |38 xor eax, eax | 38 00000013 31C0 xor eax, eax | 31 c0 | | ||
| - | |39 add eax, edi | 39 00000015 01F8 add eax, edi | 01 f8 | | ||
| - | |40 add eax, esi | 40 00000017 01F0 add eax, esi | 01 f0 | | ||
| - | |41 mov dword [resultado], | ||
| - | |42 | 42 | ||
| - | |43 pop eax | 43 0000001E 58 pop eax | 58 | | ||
| - | |44 pop esi | 44 0000001F 5E pop esi | 5e | | ||
| - | |45 pop edi | 45 00000020 5F pop edi | 5f | | ||
| - | |46 | 46 | ||
| - | |47 ret | 47 00000021 C3 ret | c3 | | ||
| - | +-----------------------------------+--------------------------------------------------------------------------+--------------------+ | ||
| - | </ | ||
| - | Fichero binario (el desplazamiento solo se presenta a titulo informativo, | ||
| - | <code c> | ||
| - | +----------------+--------------------------------------------------+ | ||
| - | | DESPLAZAMIENTO | CODIGOS DE OPERACION | ||
| - | +----------------+--------------------------------------------------+ | ||
| - | | 00000000 | ||
| - | | 00000010 | ||
| - | | 00000020 | ||
| - | | 00000030 | ||
| - | | 00000040 | ||
| - | | 00000050 | ||
| - | | * | | | ||
| - | | 00001000 | ||
| - | | 00001006 | ||
| - | +----------------+--------------------------------------------------+ | ||
| - | </ | ||
| - | |||
| - | ==== Reubicación de secciones utilizando C y ensamblador ==== | ||
| - | Un recurso frecuentemente utilizado es la mezcla de C y ensamblador en un mismo proyecto. Para tal fin el enlazador es una herramienta crucial. A continuación se ejemplifica la metodología a emplear, para utilizar funciones de escritas en C dentro de un fuente de ensamblador y relocalizarlas mediante el enlazador. Es importante observar como el código queda agrupado en forma consecutiva, | ||
| - | El fichero de enlace es // | ||
| - | <code text> | ||
| - | SECTIONS | ||
| - | { | ||
| - | .text 0x08000: | ||
| - | AT (0x000000) | ||
| - | { *(.codigo_principal); | ||
| - | |||
| - | . = 0x09000; | ||
| - | | ||
| - | | ||
| - | |||
| - | . = 0x0A000; | ||
| - | | ||
| - | | ||
| - | |||
| - | | ||
| - | AT ( LOADADDR(.text) + SIZEOF(.text)) | ||
| - | { *(.codigo_funciones); | ||
| - | |||
| - | | ||
| - | { *(.eh_frame); | ||
| - | |||
| - | } | ||
| - | </ | ||
| - | Los parámetros del enlazador | ||
| - | <code text> | ||
| - | ld -z max-page-size=0x01000 --oformat=binary -m elf_i386 -T mi_linker_script.lds -e Inicio fichero_s.elf fichero_c.elf -o fichero.bin | ||
| - | </ | ||
| - | El código y sus salidas | ||
| - | <code asm> | ||
| - | +-----------------------------------+--------------------------------------------------------------------------+--------------------+ | ||
| - | |.asm | .lst | ||
| - | +-----------------------------------+--------------------------------------------------------------------------+--------------------+ | ||
| - | | 1 bits 32 | ||
| - | | 2 | ||
| - | | 3 global Inicio | ||
| - | | 4 | ||
| - | | 5 SECTION .codigo_principal | ||
| - | | 6 | ||
| - | | 7 Inicio: | ||
| - | | 8 call [clrscr] | ||
| - | | 9 mov dword edi, 0x1 | 9 00000000 BF01000000 | ||
| - | |10 mov dword [parametro_a], | ||
| - | |11 mov dword esi, 0x3 | 11 0000000B BE03000000 | ||
| - | |12 mov dword [parametro_b], | ||
| - | |13 call far [_suma] | ||
| - | |14 mov dword eax, [resultado] | ||
| - | |15 add dword eax, [variable_a] | ||
| - | |16 mov dword [array_a], eax | 16 00000027 A3[08000000] | ||
| - | |17 | ||
| - | |18 | 18 | ||
| - | |19 SECTION .dat_inic_a progbits | ||
| - | |20 | ||
| - | |21 | ||
| - | |22 | 22 | ||
| - | |23 SECTION .dat_no_inic_a nobits1 | ||
| - | |24 | ||
| - | | | ||
| - | |25 | ||
| - | |26 | ||
| - | |27 | ||
| - | |28 | 28 | ||
| - | |29 SECTION .codigo_funciones | ||
| - | |30 | ||
| - | |31 push edi | 31 00000000 57 push edi | 57 | | ||
| - | |32 push esi | 32 00000001 56 push esi | 56 | | ||
| - | |33 push eax | 33 00000002 50 push eax | 50 | | ||
| - | |34 | 34 | ||
| - | |35 xor edi, edi | 35 00000003 31FF xor edi, edi | 31 ff | | ||
| - | |36 mov dword edi, | ||
| - | |37 xor esi, esi | 37 0000000B 31F6 xor esi, esi | 31 f6 | | ||
| - | |38 mov dword esi, | ||
| - | |39 xor eax, eax | 39 00000013 31C0 xor eax, eax | 31 c0 | | ||
| - | |40 add eax, edi | 40 00000015 01F8 add eax, edi | 01 f8 | | ||
| - | |41 add eax, esi | 41 00000017 01F0 add eax, esi | 01 f0 | | ||
| - | |42 mov dword [resultado], | ||
| - | |43 | 43 | ||
| - | |44 pop eax | 44 0000001E 58 pop eax | 58 | | ||
| - | |45 pop esi | 45 0000001F 5E pop esi | 5e | | ||
| - | |46 pop edi | 46 00000020 5F pop edi | 5f | | ||
| - | |47 | 47 | ||
| - | |48 ret | 48 00000021 C3 ret | c3 | | ||
| - | +-----------------------------------+--------------------------------------------------------------------------+--------------------+ | ||
| - | </ | ||
| - | El código fuente C que implementa la función **clrcr**, y binario asociado sin considerar la sección //exception handler frame// (eh_frame) | ||
| - | <code c> | ||
| - | #include " | ||
| - | |||
| - | void clrscr(void) | ||
| - | { | ||
| - | unsigned short cont_aux; | ||
| - | unsigned char *ptr_video_text = (unsigned char *)VIDEO_BUFFER_INI; | ||
| - | for (cont_aux=0; | ||
| - | { | ||
| - | *ptr_video_text++ = SPACE_CHAR; | ||
| - | *ptr_video_text++ = VIDEO_BCKGRND_BLACK; | ||
| - | } | ||
| - | } | ||
| - | </ | ||
| - | |||
| - | <code c> | ||
| - | +----------------+--------------------------------------------------+ | ||
| - | | DESPLAZAMIENTO | CODIGOS DE OPERACION | ||
| - | +----------------+--------------------------------------------------+ | ||
| - | |00000000 | ||
| - | |00000010 | ||
| - | |00000020 | ||
| - | |00000030 | ||
| - | +----------------+--------------------------------------------------+ | ||
| - | </ | ||
| - | |||
| - | A modo meramente informativo se presenta el ensamblador generado por //gcc// a partir de fuente .C | ||
| - | |||
| - | <code asm> | ||
| - | 00000000 < | ||
| - | 00000001 < | ||
| - | 00000003 < | ||
| - | 00000006 < | ||
| - | 0000000d < | ||
| - | 00000013 < | ||
| - | 00000015 < | ||
| - | 00000018 < | ||
| - | 0000001b < | ||
| - | 0000001f < | ||
| - | 00000022 < | ||
| - | 00000025 < | ||
| - | 00000029 < | ||
| - | 0000002e < | ||
| - | 00000034 < | ||
| - | 00000036 < | ||
| - | 00000037 < | ||
| - | </ | ||
| - | |||
| - | La cabecera en la cual se explicita la sección donde se debe ubicar el código de máquina generado a partir del fuente detallado precedentemente, | ||
| - | <code c> | ||
| - | #ifndef __LIBFC_H | ||
| - | #define __LIBFC_H | ||
| - | |||
| - | #define VIDEO_BUFFER_INI | ||
| - | #define VIDEO_TEXT_SCREEN_WITDH | ||
| - | #define VIDEO_TEXT_SCREEN_HIGH | ||
| - | #define VIDEO_BUFFER_LENGTH | ||
| - | #define VIDEO_BCKGRND_BLACK | ||
| - | #define SPACE_CHAR | ||
| - | |||
| - | void clrscr(void) __attribute__(( section(" | ||
| - | |||
| - | #endif | ||
| - | </ | ||
| - | |||
| - | Por último y para concluir este apartado se muestra el binario obtenido, en el cual es importante apreciar (ver lo indicado entre ><) como las direcciones virtuales han sido reemplazadas en las instrucciones correspondiente y son completamente diferentes a las direcciones de carga. | ||
| - | <code c> | ||
| - | +----------------+--------------------------------------------------+ | ||
| - | | DESPLAZAMIENTO | CODIGOS DE OPERACION | ||
| - | +----------------+--------------------------------------------------+ | ||
| - | |00000000 | ||
| - | |00000010 | ||
| - | |00000020 | ||
| - | |00000030 | ||
| - | |00000040 | ||
| - | |00000050 | ||
| - | |00000060 | ||
| - | |00000070 | ||
| - | |00000080 | ||
| - | |00000090 | ||
| - | |* | ||
| - | |00001000 | ||
| - | |00001010 | ||
| - | |* | ||
| - | |00002030 | ||
| - | |00002040 | ||
| - | |00002050 | ||
| - | |00002060 | ||
| - | +----------------+--------------------------------------------------+ | ||
| - | </ | ||
| - | |||
| - | --- // | ||
| - | |||
| - | ====== FAQ ====== | ||
| - | * **¡No tengo conocimiento de ensamblador, | ||
| - | Si dispone de un buen conocimiento de ELF y de como C gestiona los diferentes tipos de datos, los conceptos brindados serán comprensibles. | ||
| - | * **¡No tengo conocimiento de ensamblador ni de C, pero sé muchísimo de Java ¿puedo leer esta guía?** | ||
| - | Si puede leerla, pero no la entenderá. | ||
| - | * **¡Soy especialista en Phyton, Visual Basic y C# ¿puedo leer esta guía?** | ||
| - | {{td3: | ||
| - | |||
| - | ====== Referencias ====== | ||
| - | * [[https:// | ||
| - | * [[http:// | ||
| - | * [[td3: | ||