2009-11-19 15 views
12

ten kod ze strony Linux man:Gdzie są symbole: etext, edata i end?

#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); 
} 

gdy uruchomić program poniżej produkuje wyjście takie jak:

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

Gdzie etext, edataend zdefiniowane? Jak te symbole są przypisane wartości? Czy to przez linker czy coś innego?

+0

Która strona podręcznika? –

+1

Sprawdź 'man 3 end' –

Odpowiedz

3

Symbole te odpowiadają początkom różnych segmentów programu. Są one ustalane przez linker.

7

Uwaga: w systemie Mac OS X powyższy kod może nie działać! Zamiast tego można mieć:

#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); 
} 
2

Co GCC robi

Rozszerzanie kgiannakakis nieco więcej.

Te symbole są zdefiniowane przez PROVIDE kluczowych skryptu linkera, udokumentowany w https://sourceware.org/binutils/docs-2.25/ld/PROVIDE.html#PROVIDE

domyślnej skrypty są generowane podczas budowania Binutils i osadzony w ld wykonywalnego: pliki zewnętrzne, które mogą zostać zainstalowane w dystrybucji podobnie jak w /usr/lib/ldscripts nie są używane domyślnie.

Echo skrypt linkera do wykorzystania:

ld -verbose | less 

W binutils 2.24 zawiera:

.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) } 

więc także odkryć, że:

  • __etext i _etext będzie również pracować
  • etext nie jest końcem odcinka .text, ale .fini, który zawiera kod
  • etext nie znajduje się na końcu tego segmentu z .rodata po nim, ponieważ Binutils zrzuca wszystkie części tylko do odczytu w tym samym segmencie

PROVIDE generuje słabe symbole: jeśli zdefiniujesz te symbole w swoim kodzie C, twoja definicja wygra i ukryje ten.

Minimal Linux 32-bit przykład

Aby naprawdę zrozumieć jak to wszystko działa, chciałbym stworzyć minimalne przykłady!

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) 
    } 
} 

skompilować i uruchomić:

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

wyjściowa:

2 

Objaśnienie: sdata wskazuje na pierwszy bajt początku poniższej sekcji .data.

Tak więc kontrolując pierwszy bajt tej sekcji, kontrolujemy stan wyjścia!

This example on GitHub.