studio programacion móviles libros libro desarrollo desarrollar curso aprende aplicaciones c gcc linker ld

programacion - ¿Dónde se definen los símbolos etext, edata y end?



manual de programacion android pdf (4)

Tenga en cuenta que en Mac OS X, ¡el código anterior puede no funcionar! En su lugar, puede tener:

#include <stdio.h> #include <stdlib.h> #include <mach-o/getsect.h> int main(int argc, char *argv[]) { printf(" program text (etext) %10p/n", (void*)get_etext()); printf(" initialized data (edata) %10p/n", (void*)get_edata()); printf(" uninitialized data (end) %10p/n", (void*)get_end()); exit(EXIT_SUCCESS); }

Este es un código de la página man de Linux:

#include <stdio.h> #include <stdlib.h> extern char etext, edata, end; int main() { printf("First address past:/n"); printf(" program text (etext) %10p/n", &etext); printf(" initialized data (edata) %10p/n", &edata); printf(" uninitialized data (end) %10p/n", &end); exit(EXIT_SUCCESS); }

cuando se ejecuta, el siguiente programa produce resultados como los siguientes:

$ ./a.out First address past: program text (etext) 0x8048568 initialized data (edata) 0x804a01c uninitialized data (end) 0x804a024

¿Dónde están etext , edata end defined? ¿Cómo se les asignan los valores a esos símbolos? ¿Es por linker o algo más?


Esos símbolos corresponden a los comienzos de varios segmentos del programa. Ellos son establecidos por el enlazador.



Lo que hace GCC

La expansión de kgiannakakis es un poco más.

Esos símbolos están definidos por la palabra clave PROVIDE del script del enlazador, documentada en https://sourceware.org/binutils/docs-2.25/ld/PROVIDE.html#PROVIDE

Los scripts predeterminados se generan cuando se crean Binutils y se incrustan en el ejecutable ld : los archivos externos que se pueden instalar en su distribución, como en /usr/lib/ldscripts , no se utilizan de manera predeterminada.

Echo el script del enlazador que se utilizará:

ld -verbose | less

En binutils 2.24 contiene:

.text : { *(.text.unlikely .text.*_unlikely .text.unlikely.*) *(.text.exit .text.exit.*) *(.text.startup .text.startup.*) *(.text.hot .text.hot.*) *(.text .stub .text.* .gnu.linkonce.t.*) /* .gnu.warning sections are handled specially by elf32.em. */ *(.gnu.warning) } .fini : { KEEP (*(SORT_NONE(.fini))) } PROVIDE (__etext = .); PROVIDE (_etext = .); PROVIDE (etext = .); .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) } .rodata1 : { *(.rodata1) }

Entonces también descubrimos que:

  • __etext y _etext también funcionarán
  • etext no es el final de la sección .text , sino más bien .fini , que también contiene código
  • etext no está al final del segmento, con .rodata siguiéndolo, ya que Binutils vuelca todas las secciones de solo lectura en el mismo segmento

PROVIDE genera símbolos débiles: si también defines esos símbolos en tu código C, tu definición ganará y ocultará esta.

Ejemplo mínimo de Linux de 32 bits

Para comprender realmente cómo funcionan las cosas, ¡me gusta crear ejemplos mínimos!

main.S :

.section .text /* Exit system call. */ mov $1, %eax /* Exit status. */ mov sdata, %ebx int $0x80 .section .data .byte 2

link.ld :

SECTIONS { . = 0x400000; .text : { *(.text) sdata = .; *(.data) } }

Compilar y ejecutar:

gas --32 -o main.o main.S ld -m elf_i386 -o main -T link.ld main.o ./main echo $?

Salida:

2

Explicación: sdata apunta al primer byte del inicio de la sección .data que sigue.

Entonces, al controlar el primer byte de esa sección, ¡controlamos el estado de salida!

Este ejemplo en GitHub .