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 ?

Foi útil?

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á trabalhar
  • etext não é o fim do .text a seção, mas, ao invés de .fini, que também contém o código
  • etext 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!

Este exemplo no GitHub.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top