static int arr [10] adres pamięci zawsze kończy się na 060

17

Mam program ac, który wygląda tak

main.c

#include <stdio.h>
#define SOME_VAR 10

static int heap[SOME_VAR];


int main(void) {
    printf("%p", heap);
    return 0;
}

i wypisuje to, gdy uruchamiam skompilowany program kilka razy

0x58aa7c49060
0x56555644060
0x2f8d1f8e060
0x92f58280060
0x59551c53060
0xd474ed6e060
0x767c4561060
0xf515aeda060
0xbe62367e060

Dlaczego zawsze kończy się na 060? A czy tablica jest przechowywana w stercie?

Edycja: Jestem na Linuksie i mam włączony ASLR. Skompilowałem program za pomocą gcc

linuxlmao
źródło
2
Jaki system operacyjny Jaki kompilator?
Andrew Henle
2
Zmienna nie znajduje się w stercie, znajduje się w sekcji data lub bss przestrzeni adresowej programu, patrz en.wikipedia.org/wiki/Static_variable . Domyślam się, że program zawsze będzie umieszczany pod adresem pamięci na pewnej granicy, np. Dzielny przez 0x1000, a zmienna jest umieszczana przez kompilator pod stałym przesunięciem w przestrzeni adresowej programu.
Bodo

Odpowiedzi:

15

Adresy różnią się z powodu ASLR (ramdomizacja układu przestrzeni adresowej). Dzięki temu plik binarny można zmapować w różnych lokalizacjach w wirtualnej przestrzeni adresowej.

Zmienna heap- w przeciwieństwie do nazwy - nie znajduje się na stercie, ale na bss. Przesunięcie w przestrzeni adresowej jest zatem stałe.

Strony są odwzorowane na ziarnistość strony, która wynosi 4096 bajtów (hex: 0x1000) na wielu platformach. Z tego powodu ostatnie trzy cyfry szesnastkowe adresu są takie same.

Kiedy zrobiłeś to samo ze zmienną stosu , adres może nawet różnić się ostatnimi cyframi na niektórych platformach (mianowicie linux z najnowszymi jądrami), ponieważ stos jest nie tylko mapowany gdzie indziej, ale również otrzymuje losowe przesunięcie przy uruchomieniu.

Ctx
źródło
ASLR losuje bazę ładowania, jak pamiętam. Adres sekcji oparty jest na tym adresie.
Afshin
Korzystam z książki o obiektowym programowaniu ANSI-C autorstwa Axel-Thobias Schreiner. Książka została napisana w 1993 roku. Czy wiesz, czy wtedy układ pamięci był inny? Jeśli tak nie było, to dlaczego mógł nazwać zmienną, heapgdy nie ma jej w stosie?
linuxlmao
Czy 4096 tłumaczy się w jakiś sposób na 060, czy też 0x1000 tłumaczy się na 060, w przeciwnym razie nie rozumiem, co masz na myśli mówiąc, że to jest przyczyną zakończenia? Myślałem, że może to mieć związek z rozmiarem tablicy, która jest czymś, co jest tłumaczone na 060 w systemie szesnastkowym z, np. Dziesiętne
linuxlmao
2
@linuxlmao Przesunięcie wynosi na przykład 14060, więc gdy dodasz wielokrotność rozmiaru strony (0x1000), pozostaną trzy ostatnie cyfry 060.
Ctx
4

Jeśli używasz systemu Windows, przyczyną jest struktura PE .

Twoja heapzmienna jest przechowywana w .datasekcji pliku, a jej adres jest obliczany na podstawie początku tej sekcji. Każda sekcja jest ładowana niezależnie pod adresem, ale jej adres początkowy jest wielokrotnością rozmiaru strony. Ponieważ nie masz żadnych innych zmiennych, jego adres jest prawdopodobnie początkiem .datasekcji, więc jego adres będzie wielokrotnością wielkości porcji.

Na przykład jest to tabela skompilowanej wersji kodu systemu Windows: Sekcje.text sekcja były Twój kod jest kompilowany i .datazawiera swoją heapzmienną. Kiedy twój PE jest ładowany do pamięci, sekcje są ładowane pod innym adresem i który jest zwracany przez VirtualAlloc()i będzie wielokrotnością rozmiaru strony. Ale adres każdej zmiennej odnosi się do początku sekcji, która jest teraz rozmiarem strony. Tak więc zawsze zobaczysz stały numer na niższych cyfrach. Ponieważ względny adres heapod początku sekcji jest oparty na kompilatorze, opcjach kompilacji itp., Zobaczysz inną liczbę z tego samego kodu, ale różne kompilatory, ale za każdym razem naprawiane jest to, co zostanie wydrukowane.

Kiedy kompiluję kod, zauważyłem, że heapumieszczany jest w 0x8B0bajtach po rozpoczęciu .datasekcji. Za każdym razem, gdy uruchamiam ten kod, mój adres kończy się na0x8B0 .

Afshin
źródło
Korzystam z książki o obiektowym programowaniu ANSI-C autorstwa Axel-Thobias Schreiner. Książka została napisana w 1993 roku. Czy wiesz, czy wtedy układ pamięci był inny? Jeśli tak nie było, to dlaczego mógł nazwać zmienną, heapgdy nie ma jej w stosie?
linuxlmao
2
@linuxlmao Mogło być inaczej. W 1993 r. Windows był 16-bitowym systemem operacyjnym z segmentacją pamięci i wszystkimi mylącymi rzeczami. To nie była 32-bitowa architektura z płaską pamięcią, jak jest teraz. Ale tego rodzaju rzeczy powodują, że zadawanie / odpowiadanie na ogólne pytania dotyczące układu programu binarnego w pamięci nie jest przydatne. Zrozum, co ogólnie gwarantuje Ci język C , i to wszystko, co musisz wiedzieć. Martw się tylko o rzeczywisty układ, jeśli debugujesz konkretny problem, a następnie użyj debugera
Cody Gray
nie, zmienna nie powinna być tworzona na stercie nawet na starszych systemach, ponieważ nie została przydzielona z malloc i ma statyczny czas przechowywania
phuclv
@Ashshin Zajmuję się powyższym komentarzem OP
phuclv
@phuclv przepraszam, ponieważ nie wspomniano o nim, myślałem, że zwracasz się do mnie. :)
Afshin
4

Kompilator się położył heap z przesunięciem 0x60 bajtów w segmencie danych, który ma, być może dlatego, że kompilator ma jakieś inne rzeczy w pierwszych bajtach 0x60, takie jak dane używane przez kod uruchamiający mainprocedurę. Dlatego widzisz „060”; jest dokładnie tam, gdzie to się stało, i nie ma to wielkiego znaczenia.

Randomizacja układu przestrzeni adresowej zmienia adresy podstawowe używane dla różnych części pamięci programu, ale zawsze dzieje się tak w jednostkach 0x1000 bajtów (ponieważ pozwala to uniknąć problemów z wyrównaniem i innych problemów). Widzisz więc, że adresy zmieniają się o wielokrotności 0x1000, ale ostatnie trzy cyfry się nie zmieniają.

Definicja static int heap[SOME_VAR];określa heapstatyczny czas przechowywania. Typowe implementacje języka C przechowują je w ogólnej sekcji danych, a nie w stercie. „Sterta” jest mylącą nazwą pamięci używanej do dynamicznego przydzielania. (Jest to mylące, ponieważ mallocimplementacje mogą korzystać z różnych struktur danych i algorytmów, nie ograniczając się do stosów. Mogą nawet korzystać z wielu metod w jednej implementacji.)

Eric Postpischil
źródło