Wikipedia mentions that "the bss section typically includes all uninitialized variables declared at file scope." Given the following file:
int uninit;
int main() {
uninit = 1;
return 0;
}
When I compile this to an executable I see the bss segment filled properly:
$ gcc prog1.c -o prog1
$ size prog1
text data bss dec hex filename
1115 552 8 1675 68b prog1
However if I compile it as an object file I don't see the bss segment (I'd expect it to be 4):
$ gcc -c prog1.c
$ size prog1.o
text data bss dec hex filename
72 0 0 72 48 prog1.o
Is there something obvious I am missing?
I am using gcc version 4.8.1.
bss and add almost nothing (about 4–8 bytes for the description) to the executable file size. So the reason for . bss is to have smaller executable, saving space and allowing faster loading of the program, as the loader(startup) can just allocate a bunch of zeros instead of having to copy the data from disk. So .
bss section is used by the compiler for global and static variables. It is one of the default COFF sections that is used to reserve a specified amount of space in the memory map that can later be used for storing data. It is normally uninitialized. All global and static variables in a C program are placed in the .
In computer programming, the block starting symbol (abbreviated to . bss or bss) is the portion of an object file, executable, or assembly language code that contains statically allocated variables that are declared but have not been assigned a value yet. It is often referred to as the "bss section" or "bss segment".
'text' is my code, vector table plus constants. 'data' is for initialized variables, and it counts for RAM and FLASH. The linker allocates the data in FLASH which then is copied from ROM to RAM in the startup code. 'bss' is for the uninitialized data in RAM which is initialized with zero in the startup code.
If we use readelf -s
to look at the symbol table, we'll see:
$ readelf -s prog1.o
Symbol table '.symtab' contains 10 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS bss.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
7: 0000000000000000 0 SECTION LOCAL DEFAULT 5
8: 0000000000000004 4 OBJECT GLOBAL DEFAULT COM uninit <<<<
9: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 main
We see that your uninit
symbol ("variable") is, at this stage, a "common" symbol. It has not yet been "assigned" to the BSS.
See this question for more information on "common" symbols: What does "COM" means in the Ndx column of the .symtab section?
Once your final executable is linked together, it will be put in the BSS as you expected.
You can bypass this behavior by passing the -fno-common
flag to GCC:
$ gcc -fno-common -c bss.c
$ size bss.o
text data bss dec hex filename
72 0 4 76 4c bss.o
Instead, you could mark uninit
as static
. This way, the compiler will know that no other .o
file can refer to it, so it will not be a "common" symbol. Instead, it will be placed into the BSS immediately, as you expected:
$ cat bss.c
static int uninit;
int main() {
uninit = 1;
return 0;
}
$ gcc -c bss.c
$ size bss.o
text data bss dec hex filename
72 0 4 76 4c bss.o
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With