O endereço de memória estática int arr [10] sempre termina em 060

17

Eu tenho um programa ac que se parece com isso

main.c

#include <stdio.h>
#define SOME_VAR 10

static int heap[SOME_VAR];


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

e gera isso quando executo o programa compilado algumas vezes

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

Por que sempre termina em 060? E a matriz é armazenada na pilha?

Edit: Estou no Linux e tenho o ASLR ativado. Eu compilei o programa usando o gcc

linuxlmao
fonte
2
Qual sistema operacional? Qual compilador?
Andrew Henle
2
A variável não está na pilha, está na seção de dados ou bss do espaço de endereço do programa, consulte en.wikipedia.org/wiki/Static_variable . Meu palpite é que o programa sempre será colocado em um endereço de memória em um determinado limite, por exemplo, divisível por 0x1000, e a variável é colocada pelo compilador em um deslocamento fixo no espaço de endereço do programa.
Bodo

Respostas:

15

Os endereços diferem devido ao ASLR (ramdomization do layout do espaço de endereço). Usando isso, o binário pode ser mapeado em diferentes locais no espaço de endereço virtual.

A variável heap- ao contrário do nome - não está localizada na pilha, mas na bss. O deslocamento no espaço de endereço é, portanto, constante.

As páginas são mapeadas na granularidade da página, que é 4096 bytes (hex: 0x1000) em várias plataformas. Esta é a razão pela qual os últimos três dígitos hexadecimais do endereço são iguais.

Quando você fez o mesmo com uma variável de pilha , o endereço pode até variar nos últimos dígitos em algumas plataformas (ou seja, linux com kernels recentes), porque a pilha não é apenas mapeada em outro lugar, mas também recebe um deslocamento aleatório na inicialização.

Ctx
fonte
ASLR aleatoriamente base de carregamento como eu me lembro. O endereço da seção é baseado nesse endereço.
Afshin
Estou usando um livro sobre programação ANSI-C orientada a objetos de Axel-Thobias Schreiner. O livro foi escrito em algo como 1993. Você sabia se o layout da memória era diferente naquela época? Caso contrário, por que ele poderia nomear a variável heapquando ela não está na pilha?
linuxlmao
O 4096 se traduz em 060 de alguma forma ou 0x1000 se traduz em 060, caso contrário, não entendo o que você quer dizer com esse é o motivo do final? Eu pensei que poderia ter a ver com o tamanho da matriz ser algo que é traduzido em 060 em hexadecimal de, por exemplo decimal
linuxlmao 11/02
2
@linuxlmao O deslocamento é, por exemplo, 14060, portanto, quando você adiciona um múltiplo de um tamanho de página (0x1000), os últimos três dígitos permanecem 060.
Ctx 11/02
4

Se você estiver usando o Windows, o motivo é a estrutura do PE .

Sua heapvariável é armazenada na .dataseção do arquivo e seu endereço é calculado com base no início desta seção. Cada seção é carregada em um endereço independentemente, mas seu endereço inicial é múltiplo do tamanho da página. Como você não possui outras variáveis, o endereço provavelmente é o início da .dataseção, portanto, o endereço terá vários tamanhos de bloco.

Por exemplo, esta é a tabela da versão compilada do Windows para o seu código: Seções A .textseção é onde seu código compilado é e .datacontém sua heapvariável. Quando o seu PE é carregado na memória, as seções são carregadas em um endereço diferente e retornado VirtualAlloc()e será múltiplo do tamanho da página. Mas o endereço de cada variável é relativo ao início da seção que agora é do tamanho de uma página. Então você sempre verá um número fixo nos dígitos inferiores. Como o endereço relativo heapdo início da seção é baseado no compilador, nas opções de compilação, etc., você verá um número diferente do mesmo código, mas diferentes compiladores, mas sempre que o que será impresso for corrigido.

Ao compilar o código, notei que ele heapé colocado em 0x8B0bytes após o início da .dataseção. Portanto, toda vez que executo esse código, meu endereço termina 0x8B0.

Afshin
fonte
Estou usando um livro sobre programação ANSI-C orientada a objetos de Axel-Thobias Schreiner. O livro foi escrito em algo como 1993. Você sabia se o layout da memória era diferente naquela época? Caso contrário, por que ele poderia nomear a variável heapquando ela não está na pilha?
linuxlmao
2
@linuxlmao Pode muito bem ter sido diferente. Em 1993, o Windows era um sistema operacional de 16 bits, com segmentação de memória e todo tipo de coisas confusas. Era não de 32 bits, arquitetura de memória simples de como é agora. Mas é por isso que perguntar / responder a perguntas gerais sobre o layout de um programa binário na memória não é útil. Entenda o que o padrão da linguagem C garante a você em geral e é tudo o que você precisa saber. Apenas se preocupe com o layout real se você estiver depurando um problema específico e use um depurador
Cody Gray
não, a variável não deve ser criada no heap, mesmo em sistemas mais antigos, porque não foi alocada com malloc e possui duração de armazenamento estático
phuclv
@Afshin Estou abordando o comentário do OP acima
phuclv 12/02
@ phuclv desculpe, porque você não foi mencionado ele, eu pensei que você estava me abordando. :)
Afshin
4

Por acaso, o compilador colocou heapno deslocamento 0x60 bytes em um segmento de dados, possivelmente porque o compilador possui outras coisas nos primeiros 0x60 bytes, como dados usados ​​pelo código que inicia a mainrotina. É por isso que você vê "060"; é exatamente onde aconteceu, e não há grande significado para isso.

A randomização do layout do espaço de endereço altera os endereços base usados ​​para várias partes da memória do programa, mas sempre o faz em unidades de 0x1000 bytes (porque isso evita causar problemas de alinhamento e outros problemas). Portanto, você vê os endereços flutuarem por múltiplos de 0x1000, mas os últimos três dígitos não são alterados.

A definição static int heap[SOME_VAR];define heapcom a duração do armazenamento estático. As implementações típicas de C o armazenam em uma seção de dados geral, não na pilha. O "heap" é um nome impróprio para memória usada para alocação dinâmica. (É um nome impróprio, porque as mallocimplementações podem usar uma variedade de estruturas e algoritmos de dados, não se limitando a pilhas. Eles podem até usar vários métodos em uma implementação.)

Eric Postpischil
fonte