From my book:
.bss:
Uninitialized global C variables
COMMON:
Uninitalized data objects that are n
Commons only appear before the linking stage. Commons are what later goes into the bss or data‚ but it's up to the linker to decide where it goes. This allows you to have the same variable defined in different compilation units. As far as I know this is mostly to allow some ancient header files that had int foo; in them instead of extern int foo;.
Here's how it works:
$ cat > a.c
int foo;
$ cat > b.c
int foo;
$ cat > main.c
extern int foo;
int main(int argc, char **argv) { return foo; }
$ cc -c a.c && cc -c b.c && cc -c main.c && cc -o x a.o b.o main.o
$ objdump -t a.o | grep foo
0000000000000004 O *COM* 0000000000000004 foo
$ objdump -t b.o | grep foo
0000000000000004 O *COM* 0000000000000004 foo
$ objdump -t x | grep foo
0000000000600828 g O .bss 0000000000000004 foo
$
Notice that this only works when at most one of the variables in the different compilation units is initialized.
$ echo "int foo = 0;" > a.c
$ cc -c a.c && cc -c b.c && cc -c main.c && cc -o x a.o b.o main.o
$ echo "int foo = 0;" > b.c
$ cc -c a.c && cc -c b.c && cc -c main.c && cc -o x a.o b.o main.o
b.o:(.bss+0x0): multiple definition of `foo'
a.o:(.bss+0x0): first defined here
collect2: ld returned 1 exit status
$
This is scary stuff, compatibility with ancient systems and you should never rely on it. Do things properly - only one definition of global variables in all compilation units, declare it extern it everywhere else through a header.