4

Please go through the following program -

#include <stdio.h>  
void main()
{
}

Memory allocated for each segment is as follows(by using size command on Unix)-

   text    data     bss     dec     hex filename
   1040     484      16    1540     604 try

After declaration of global variable-

#include <stdio.h>
int i;

void main()
{
}

Memory allocated for each segment is as follows(by using size command on Unix) Here variable 'i' has received memory in BSS(previously it was 16 and now it is 24)-

   text    data     bss     dec     hex filename
   1040     484      24    1548     60c try

After declaration of global variable and initializing it with 10-

#include <stdio.h>
int i=10;

void main()
{
}

Memory allocated for each segment is as follows(by using size command on Unix) Here variable 'i' has received memory in data segment(previously it was 484 and now it is 488)-

   text    data     bss     dec     hex filename
   1040     488      16    1544     608 try

My question is why the global variable 'i' got the memory of size 8 bytes when it was stored in BSS but got 4 bytes when it was stored in data segment? Why there is the difference in allocating memory to an integer in BSS and data segment?

Ganeshdip
  • 389
  • 2
  • 10
  • Maybe it's an alignment requirement. – Stargateur Nov 23 '17 at 12:25
  • On my machine (x86_64 Linux), the .bss size appears to be round (downards) to multiples of 8, while the .data section doesn't. IOW, I need to add two uninitialized ints to see an increase by 8, but each initialized ints bumps the .data section by 4. Interesting question. – Petr Skocik Nov 23 '17 at 12:32
  • Uninitialized variables go in the `bss` section that should be a virtual section simply holding a counter of the space to reserve, but aligned on a boundary as requested by the binary format. When declaring an initialized variable it init value is stored in the data section, making it grow by 4bytes for a 32bits `int`. This will eventually change the position on the `bss` section that follows in the file image. if the alignment spacing is counted as `bss` space the value will change. – Frankie_C Nov 23 '17 at 12:48
  • As all this is highly compiler dependant, you should say what compiler and version you use along with the compiler flags. – Serge Ballesta Nov 23 '17 at 12:52
  • @Frankie_C global and static are initialized by 0 by default. – Stargateur Nov 23 '17 at 13:25
  • @Stargateur `bss` is a 'virtual' (empty) section, generally zeroed (even if not all compilers support this) programmatically in C runtime initialization after that the loader allocated the memory specified in the `bss` header in the process space. – Frankie_C Nov 23 '17 at 13:53
  • @Frankie_C thank for the information :) – Stargateur Nov 23 '17 at 14:01
  • @rsp- The question you mentioned is same but still the answer is not given for memory allocation in BSS and data segment. The most voted answer only says it takes same memory in both segment. But why 4 bytes more are added in case of BSS. Still not clear. – Ganeshdip Nov 23 '17 at 14:10

1 Answers1

2

why the global variable 'i' got the memory of size 8 bytes when it was stored in BSS but got 4 bytes when it was stored in data segment?

First, why 4 bytes in data segment?

As many folks already answered this - The .data segment contains any global or static variables that are initialized beforehand. An integer is of 4 bytes in size and that is reflecting in data segment size when you have global int i=10; in your program.

Now, why 8 bytes in .bss segment?

You are observing this behavior because of the default linker script of GNU linker GNU ld. You can get information about linker script here.

While linking, GNU linker (GNU ld) is using the default linker script.

The default linker script specifies the alignment for .bss segment.

If you want to see the default linker script, you can do it using command -

gcc -Wl,-verbose main.c

The output of this gcc command will contain following statement:

using internal linker script:
==================================================
// The content between these two lines is the default linker script
==================================================

In the default linker script, you can find the .bss section:

  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we don't
      pad the .data section.  */
   . = ALIGN(. != 0 ? 64 / 8 : 1);
  }

Here, you can see . = ALIGN(. != 0 ? 64 / 8 : 1); which indicates the default alignment as 8 bytes.

The program:

#include <stdio.h>
int i;

void main()
{
}

when built with default linker script, 'i' get the memory of size 8 bytes in BSS because of 8 bytes alignment:

# size a.out
   text    data     bss     dec     hex filename
   1040     484      24    1548     60c a.out

[bss = 24 bytes (16 + 8)]

GNU linker provides a provision to pass your own linker script to it and in that case, it uses the script passed to it to build the target instead of default linker script.

Just to try this, you can copy the content of default linker script in a file and use this command to pass your linker script to GNU ld:

gcc -Xlinker -T my_linker_script main.c

Since you can have your own linker script, so you can make changes in it and see the change in behavior.

In the .bss section, change this . = ALIGN(. != 0 ? 64 / 8 : 1); to . = ALIGN(. != 0 ? 32 / 8 : 1);. This will change the default alignment from 8 bytes to 4 bytes. Now build your target using linker script with this change.

The output is:

# size a.out
   text    data     bss     dec     hex filename
   1040     484      20    1544     608 a.out

Here you can see bss size is 20 bytes (16 + 4) because of 4 bytes alignment.

Hope this answer your question.

H.S.
  • 11,654
  • 2
  • 15
  • 32