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.
Estos símbolos se definen en un archivo de secuencia de comandos del 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!