After a day of debugging, I found that the compiler does not put my uninitialized globally-exported symbols under BSS; it marks them as "common symbols" instead. This has lead to disastrous effects cause I use the following innocent-looking segment in the linker script:
Code: Select all
SECTIONS {
...
.bss : {
__bss_start = .;
*(EXCLUDE_FILE (*head.o *e820.o) .bss)
__bss_end = .;
}
__kernel_end = .;
}
The best explanation of "common symbols" I found was from Ulrich Drepper's paper (archive) on shared libraries, mainly the following part:
There are also further information about those symbols from Ian Lance Taylor's articles series on linkers here (archive), on nm(1) manpage, and on ld(1) about LD's very useful `--warn-common' flag. You can also check some nice historical details from this binutils bugreport (archive). To let your BSS variable boundaries really reflect your entire BSS space, you either need to use GCC's '--no-common' parameter or modify the above script segment to:Common variables are widely used in fortran, but they got used in C and C++ as well to work around mistakes of programmers. Since in the early days people used to drop the 'extern' keyword from variable definitions, in the same way it is possible to drop it from function declaration, the compiler often had multiple definitions of the same variable in different files.
To help the poor and clueless programmer, the C/C++ compiler normally generates common variables for uninitialized definitions such as `int foo;' For common variables, there can be more than one definition, and they all get unified in one location in the output file .. their values does not need to be stored in the ELF file.
Code: Select all
SECTIONS {
...
.bss : {
__bss_start = .;
*(EXCLUDE_FILE (*head.o *e820.o) .bss)
*(EXCLUDE_FILE (*head.o *e820.o) COMMON)
__bss_end = .;
}
__kernel_end = .;
}
Thanks all