Por que o segmento .bss é necessário?

O que eu sei é que variables ​​globais e estáticas são armazenadas no segmento .data , e os dados não inicializados estão no segmento .bss . O que não entendo é por que temos segmento dedicado para variables ​​não inicializadas? Se uma variável não inicializada tiver um valor atribuído em tempo de execução, a variável ainda existe apenas no segmento .bss ?

No programa a seguir, a está no segmento .data e b está no segmento .bss ; isso é correto? Por favor, corrija-me se o meu entendimento estiver errado.

 #include  #include  int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9}; int b[20]; /* Uninitialized, so in the .bss and will not occupy space for 20 * sizeof (int) */ int main () { ; } 

Além disso, considere seguir o programa,

 #include  #include  int var[10]; /* Uninitialized so in .bss */ int main () { var[0] = 20 /* **Initialized, where this 'var' will be ?** */ } 

O motivo é reduzir o tamanho do programa. Imagine que seu programa C seja executado em um sistema embarcado, onde o código e todas as constantes sejam salvos na verdadeira ROM (memory flash). Em tais sistemas, um “copy-down” inicial deve ser executado para definir todos os objects de duração de armazenamento estático, antes de main () ser chamado. Normalmente, ele irá como este pseudo:

 for(i=0; i 

Onde .data e .bss são armazenados na RAM, mas init_value é armazenado na ROM. Se tivesse sido um segmento, então a ROM tinha que ser preenchida com muitos zeros, aumentando significativamente o tamanho da ROM.

Os executáveis ​​baseados em RAM funcionam de maneira semelhante, embora, é claro, eles não possuam ROM verdadeira.

Além disso, memset é provavelmente um montador inline muito eficiente, o que significa que o copy-down de boot pode ser executado mais rapidamente.

O segmento .bss é uma otimização. O segmento .bss inteiro é descrito por um único número, provavelmente 4 bytes ou 8 bytes, que fornece seu tamanho no processo em execução, enquanto a seção .data é tão grande quanto a sum dos tamanhos das variables ​​inicializadas. Assim, o .bss torna os executáveis ​​menores e mais rápidos para carregar. Caso contrário, as variables ​​poderiam estar no segmento .data com boot explícita para zeros; o programa seria difícil de dizer a diferença. (Em detalhes, o endereço dos objects em .bss provavelmente seria diferente do endereço se estivesse no segmento .data .)

No primeiro programa, a seria no segmento .data b estaria no segmento .bss do executável. Depois que o programa é carregado, a distinção se torna irrelevante. Em tempo de execução, b ocupa 20 * sizeof(int) bytes.

No segundo programa, var é alocado espaço e a atribuição em main() modifica esse espaço. Acontece que o espaço para var foi descrito no segmento .bss em vez do segmento .data , mas isso não afeta a maneira como o programa se comporta durante a execução.

Bem, em primeiro lugar, essas variables ​​no seu exemplo não são não inicializadas; C especifica que variables ​​estáticas não inicializadas de outra forma são inicializadas como 0.

Portanto, a razão para .bss é ter executáveis ​​menores, economizando espaço e permitindo um carregamento mais rápido do programa, já que o carregador pode simplesmente alocar um monte de zeros em vez de ter que copiar os dados do disco.

Ao executar o programa, o carregador de programas carregará .data e .bss na memory. Escreve em objects que residem em .data ou .bss, portanto, só vão para a memory, eles não são liberados para o binário no disco a qualquer momento.

De Assembly Language Step-by-Step: Programação com Linux por Jeff Duntemann, sobre a seção .data :

A seção .data contém definições de dados de itens de dados inicializados. Dados inicializados são dados que possuem um valor antes de o programa começar a ser executado. Esses valores fazem parte do arquivo executável. Eles são carregados na memory quando o arquivo executável é carregado na memory para execução.

O importante a lembrar sobre a seção .data é que quanto mais itens de dados inicializados você definir, maior será o arquivo executável e mais tempo levará para carregá-lo do disco para a memory quando você executá-lo.

e a seção .bss :

Nem todos os itens de dados precisam ter valores antes de o programa começar a ser executado. Quando você está lendo dados de um arquivo de disco, por exemplo, você precisa ter um local para os dados serem enviados depois do disco. Buffers de dados como esse são definidos na seção .bss do seu programa. Você separa um certo número de bytes para um buffer e dá um nome ao buffer, mas você não diz quais valores devem estar presentes no buffer.

Há uma diferença crucial entre os itens de dados definidos na seção .data e os itens de dados definidos na seção .bss: os itens de dados na seção .data são adicionados ao tamanho do arquivo executável. Itens de dados na seção .bss não. Um buffer que ocupa 16.000 bytes (ou mais, às vezes muito mais) pode ser definido em .bss e adicionar quase nada (cerca de 50 bytes para a descrição) ao tamanho do arquivo executável.

O artigo da Wikipédia .bss fornece uma boa explicação histórica, dado que o termo é de meados dos anos 50 (yippee my birthday ;-).

No passado, cada pedacinho era precioso, então qualquer método para sinalizar o espaço vazio reservado era útil. Este ( .bss ) é o que ficou preso.

As seções .data são para o espaço que não é vazio, em vez disso, ele terá (seus) valores definidos inseridos nele.

O System V ABI 4.1 (1997) (especificação AKA ELF) também contém a resposta:

.bss Esta seção contém dados não inicializados que contribuem para a imagem de memory do programa. Por definição, o sistema inicializa os dados com zeros quando o programa começa a ser executado. A seção não ocupa espaço no arquivo, conforme indicado pelo tipo de seção, SHT_NOBITS .

diz que o nome da seção .bss é reservado e tem efeitos especiais, em particular, ele não ocupa espaço no arquivo , portanto, a vantagem sobre .data .

A desvantagem é que todos os bytes devem ser definidos como 0 quando o sistema operacional os coloca na memory, o que é mais restritivo, mas é um caso de uso comum e funciona bem para variables ​​não inicializadas.

A documentação do tipo de seção SHT_NOBITS repete essa afirmação:

sh_size Esse membro fornece o tamanho da seção em bytes. A menos que o tipo de seção seja SHT_NOBITS , a seção ocupa bytes sh_size no arquivo. Uma seção do tipo SHT_NOBITS pode ter um tamanho diferente de zero, mas não ocupa espaço no arquivo.

O padrão C não diz nada sobre seções, mas podemos facilmente verificar onde a variável está armazenada no Linux com objdump e readelf , e concluir que globals não inicializados são de fato armazenados no .bss , veja por exemplo esta resposta: https: // stackoverflow .com / a / 36725211/895245