Onde estão os símbolos etext ,edata e fim definidos?
Pergunta
Este é um código de Linux página de man:
#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);
}
quando executado, o programa abaixo produz uma saída como o seguinte:
$ ./a.out
First address past:
program text (etext) 0x8048568
initialized data (edata) 0x804a01c
uninitialized data (end) 0x804a024
Onde estão etext
, edata
end
definido ?Como os símbolos são atribuídos valores ?É, pois, por vinculador ou algo mais ?
Solução
Esses símbolos são definidos em um Arquivo de script de ligação.
Outras dicas
Observe que no Mac OS X, o código acima pode não funcionar! Em vez disso, você pode ter:
#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);
}
Esses símbolos correspondem ao início de vários segmentos de programa. Eles são definidos pelo vinculador.
O que o GCC faz
Expansão kgiannakakis um pouco mais.
Os símbolos são definidos pelo PROVIDE
palavra-chave do linker script, documentadas em https://sourceware.org/binutils/docs-2.25/ld/PROVIDE.html#PROVIDE
O padrão scripts são gerados quando você construir o Binutils, e incorporada a ld
executável:arquivos externos que podem ser instalados em sua distribuição, como no /usr/lib/ldscripts
não são usados por padrão.
Echo o linker script a ser usado:
ld -verbose | less
No pacote binutils 2.24 contém:
.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) }
Assim também descobrimos que:
__etext
e_etext
também irá trabalharetext
não é o fim do.text
a seção, mas, ao invés de.fini
, que também contém o códigoetext
não é no final do segmento, com.rodata
a seguir, desde Binutils despeja todos os readonly seções para o mesmo segmento
PROVIDE
gera fraco símbolos:se você também define os símbolos no seu código C, a sua definição vai ganhar e ocultar este.
O mínimo de Linux de 32 bits exemplo
Para realmente entender como as coisas funcionam, eu gostaria de criar um mínimo de exemplos!
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)
}
}
Compile e execute:
gas --32 -o main.o main.S
ld -m elf_i386 -o main -T link.ld main.o
./main
echo $?
Saída:
2
Explicação: sdata
aponta para o primeiro byte de início do .data
a secção que se segue.
Assim, controlando-se o primeiro byte da seção, nós controlar o status de saída!