Where _bss_start is define Linux kernel source - linux-kernel

I am going through Linux kernel source and found _bss_start C varianle in one of assembly files but could not find where it is actully defined and intialized.
It looks like _bss_start is the starting address of the bss segment but where and how it is intialized with values in kernel source ,I am looking into linux source 2.6.25.
I looked into file asm-generic/section.h where it is defined like below
extern char _bss_start[]
but how _bss_start is defined ,is it DS register is being used to intialized it

__bss_start is defined and initialized by the linker. It references the .bss section of the image, which contains statically allocated variables.
Here is a stripped down example of the linker script defining these symbols:
.bss : {
__bss_start = .; /*< __bss_start references this position in the file */
*(.bss) /*< The actual contents of the section */
*(COMMON) /*< The actual contents of the section */
_ebss = . ; /*< _ebss references this position in the file */
}

Related

Is there a way to force a variable to be placed at top of .bss section?

I am using GCC on a Cortex M0 from NXP.
I have a non-initialized buffer which needs to be placed at 512 byte boundary due to DMA access restrictions:
DMA_CH_DESCRIPTOR_T __attribute__ ((aligned (512))) Chip_DMA_Table[MAX_DMA_CHANNEL];
This will end up in .bss section, but of course, due to alignment, there will be some lost space before. I know that .bss starts (in my MCU) at 0x10000000 which is already 512 aligned.
So the big question is how can I force my buffer to be the first symbol in .bss ?
I already tried like this but it doesn't work
.bss : ALIGN(4)
{
_bss = .;
PROVIDE(__start_bss_RAM = .) ;
PROVIDE(__start_bss_SRAM = .) ;
drv_dma.o (.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4) ;
_ebss = .;
PROVIDE(__end_bss_RAM = .) ;
PROVIDE(__end_bss_SRAM = .) ;
PROVIDE(end = .);
} > SRAM AT> SRAM
Note: I can see several potential resolves:
defining my own .bss_top for example, and modify my startup script to consider it as a separate .bss and initialize it.
defining a separate section BEFORE actual .bss and initialize my buffer from code somewhere
memset(...)
But I said it's worth to ask, maybe there is a simple linker catch on this one.
Thank you,

Global variable symbols is incorrect when I debug a unix-like kernel wrote by myself

code is here at commit #489ee1c
I am writing a unix-like kernel following this tutorial for personal learning. Global variable symbols is incorrect when I debug a unix-like kernel wrote by myself.
I start the kernel using
qemu-system-i386 -d cpu_reset -s -S -D ./run.log -drive format=raw,file=os_image -m 8G
there is also a problem that physical memory is only 3GB in code while I set -m 4G.
and start a gdb stoping at init_global_mm_vars() functions
.gdbinit
set arch i386
symbol-file /root/os/2-kernel/kernel.elf
b init_global_mm_vars
target remote localhost:1234
You can see that the address of symbol Kernel_Vmm_End is 0x58d4 ,but used in asm is 0x68d4. all above global variable symbols is incorrect.
Why all the global variable symbols go wrong ?
I found that if I don't use link.ld script and just use -Ttext=0,when link and all problems seem gone.
ENTRY(kernel_main) /* Kernel entry label */
OUTPUT_FORMAT("elf32-i386")
OUTPUT_ARCH(i386)
SECTIONS {
. = 0x0; /* Kernel code is located at 0x0 */
Kernel_Text_Vmm_Start_p = .; /* Export labels */
.text : /* Align at 4KB and load at 4KB */
{
*(.text) /* All text sections from all files */
}
. = ALIGN(0x1000);
Kernel_Rodata_Vmm_Start_p =.;
.rodata ALIGN (0x1000) : AT(ADDR(.rodata)) /* Align at 4KB and load at 4KB */
{
*(.rodata) /* All read-only data sections from all files */
}
. = ALIGN(0x1000);
Kernel_Data_Vmm_Start_p =.;
.data ALIGN (0x1000) : AT(ADDR(.data)) /* Align at 4KB and load at 4KB */
{
*(.data) /* All data sections from all files */
}
. = ALIGN(0x1000);
Kernel_Bss_Vmm_Start_p =.;
.bss ALIGN (0x1000) : AT(ADDR(.bss)) /* Align at 4KB and load at 4KB */
{
*(COMMON) /* All COMMON sections from all files */
*(.bss) /* All bss sections from all files */
}
. = ALIGN(0x1000);
Kernel_Vmm_End_p = .;
}
Still have no idea why this ld script goes wrong?

undefined reference to _GLOBAL_OFFSET_TABLE_ (only when generating binaries)

this is the problem:
When I link my scripts in C, using ld, when I generate elf32-i386 files as output format in ld, putting it as OUTPUT_FORMAT() in the ld script, I dont have any error, but if I try to put in this last OUTPUT_FORMAT() "binary" or try to output a file with .bin extension, I get a mixture of errors like:
kernel.o: In function `k_main':
kernel.c:(.text+0xe): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelutils.o: In function `k_clear_screen':
kernelutils.c:(.text+0xc): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelutils.o: In function `k_clear_screen_front':
kernelutils.c:(.text+0x56): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelutils.o: In function `k_printf':
kernelutils.c:(.text+0xa0): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelutils.o: In function `k_sleep_3sec':
kernelutils.c:(.text+0x152): undefined reference to `_GLOBAL_OFFSET_TABLE_'
kernelmalloc.o:kernelmalloc.c:(.text+0xc): more undefined references to `_GLOBAL_OFFSET_TABLE_' follow
This not only happens when compiling specific scripts, all scripts that try to use ld to link, or gcc since this calls ld, die in the attempt of get a binary with .bin extension.
When showing the symbols of one of the executables (kernel.o in the output of above) I see that the symbol _GLOBAL_OFFSET_TABLE_ isnt defined, and the most scary part, all the functions that returned error in the error output of above have their symbols removed, this is the nm output:
cristian#mymethodman:~/Desktop/kernel/0.0.3/Archivos$ nm kernel.o
U _GLOBAL_OFFSET_TABLE_
U k_clear_screen
U k_clear_screen_front
00000000 T k_main
U k_malloc
U k_printf
U k_sleep_3sec
00000000 T __x86.get_pc_thunk.bx
How I can solve this? I will leave the linker script below to ensure it isn a problem of the .ld file, with both "to get elf" and "to get binary" versions. Thanks in advance!
Ld scripts:
To get binary:
ENTRY(loader)
OUTPUT_FORMAT(binary)
SECTIONS {
/* The kernel will live at 3GB + 1MB in the virtual
address space, which will be mapped to 1MB in the
physical address space. */
. = 0xC0100000;
.text : AT(ADDR(.text) - 0xC0000000) {
*(.text)
*(.rodata*)
}
.data ALIGN (0x1000) : AT(ADDR(.data) - 0xC0000000) {
*(.data)
}
.bss : AT(ADDR(.bss) - 0xC0000000) {
_sbss = .;
*(COMMON)
*(.bss)
_ebss = .;
}
}
To get ELF:
ENTRY(loader)
OUTPUT_FORMAT(elf32-i386)
SECTIONS {
/* The kernel will live at 3GB + 1MB in the virtual
address space, which will be mapped to 1MB in the
physical address space. */
. = 0xC0100000;
.text : AT(ADDR(.text) - 0xC0000000) {
*(.text)
*(.rodata*)
}
.data ALIGN (0x1000) : AT(ADDR(.data) - 0xC0000000) {
*(.data)
}
.bss : AT(ADDR(.bss) - 0xC0000000) {
_sbss = .;
*(COMMON)
*(.bss)
_ebss = .;
}
}
As yo ucan see between both only changes the OUTPUT_FORMAT() line.
Your toolchain probably defaults to generating position-independent executables (PIE). Try compiling with gcc -fno-pie.
If you want to keep PIE for security reasons, you'll need a more complicated linker script and something that performs the initial relocation (such as a dynamic linker, but simpler constructions are possible as well).

Relocate specific object files of a library

I have a GCC project for ARM Cortex-M3. The linker script defines where each source section has to be located. So I have sections like this
.text :
{
*(.text)
} > FLASH
_sidata = .;
.data : AT (_sidata)
{
_sdata = .;
*(.data)
_edata = .;
}
The project consumes the library lib.a that contains the object file object.o and other.o. Now I want that the .text section of object.o shall be placed between _sdata and _edata. The objectiv is that these section would be copied by the startup code from the FLASH to RAM and it will be executed there. The other.o shall not be placed in that section since it's too large.
I tried it like in this SO question
.data : AT (_sidata)
{
_sdata = .;
*(.data)
object.o(.text)
_edata = .;
}
But this fails since the object.o is taken from a library and is not direct available.
I found it by myself. The library must be specififed.
.data : AT (_sidata)
{
_sdata = .;
*(.data)
lib.a:object.o(.text)
_edata = .;
}

why TI starterware examples do not clear correctly BSS segment when compiled using CodeSourcery gcc

I ran into severe troubles with beaglebone running TI AM3359arm. I'm using code sourcery to compile code. I tried to compile one of the examples, called enet_lwip, which uses lightweight IP (lwip) to provide http server.
The application crashes at certain point. By debugging I have found, that it is this piece of code, which is responsible for it:
unsigned int lwIPInit(LWIP_IF *lwipIf)
{
struct ip_addr ip_addr;
struct ip_addr net_mask;
struct ip_addr gw_addr;
unsigned int *ipAddrPtr;
static unsigned int lwipInitFlag = 0;
unsigned int ifNum;
unsigned int temp;
/* do lwip library init only once */
if(0 == lwipInitFlag)
{
lwip_init();
}
A very funny thing happens to this: one would expect, that lwipInitFlag gets initialized to 0 and hence the function calls lwip_init();
Well, this does not happen even the very first time the lwIPInit function gets called. The reason for this is, that the variable lwipInitFlag is not set to 0.
I would like to know why this is. If such initialization appears in the code, compiler should generate sequence to null it. But probably because it is preceded by static modifier, it leaves it 'as is'. Why?
The lwipInitFlag is in .bss linker section, which points to DDR memory. How can I assure, that such static assignments get initialized?
For the moment I'll hack the code for lwIP to see if this works, but it is just a warning for me, that there might be another statically declared variables somewhere in the libraries, which do not get initialized.
Any hint how to resolve this?
Adding more information to this: after your fruitful hints I think I have even more mess in how it should work. So: It is true, that I do not call/link crt*.o. On the other hand the TI starterware platform contains initialization asm source, which DOES BSS cleanup. It does it between addresses _bss_start and _bss_end.
When looking into linker script, everything looks pretty ordinary:
SECTIONS
{
. = 0x80000000;
. = ALIGN(4);
.startcode :
{
*init.o (.text)
}
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.data :
{
*(.data)
}
. = ALIGN(4);
_bss_start = .;
.bss :
{
*(.bss)
}
. = ALIGN(4);
_bss_end = .;
_stack = 0x87FFFFF8;
}
So _bss_start is address before BSS block and _bss_end is at the end of the block. The trouble is what map the Codesourcery generates.
When looking at the end of BSS in generated map file, I can see this:
COMMON 0x80088f0c 0x200 ../binary/armv7a/gcc/am335x/system_config/Debug/libsystem_config.a(interrupt.o)
0x80088f0c fnRAMVectors
0x8008910c . = ALIGN (0x4)
0x8008910c _bss_end = .
0x87fffff8 _stack = 0x87fffff8
LOAD ../binary/armv7a/gcc/am335x/drivers/Debug/libdrivers.a
LOAD ../binary/armv7a/gcc/utils/Debug/libutils.a
LOAD ../binary/armv7a/gcc/am335x/beaglebone/platform/Debug/libplatform.a
LOAD ../binary/armv7a/gcc/am335x/system_config/Debug/libsystem_config.a
LOAD /opt/CodeSourcery/arm-none-eabi/lib//libc.a
LOAD /opt/CodeSourcery/lib/gcc/arm-none-eabi/4.5.2//libgcc.a
LOAD ../binary/armv7a/gcc/am335x/drivers/Debug/libdrivers.a
LOAD ../binary/armv7a/gcc/utils/Debug/libutils.a
LOAD ../binary/armv7a/gcc/am335x/beaglebone/platform/Debug/libplatform.a
LOAD ../binary/armv7a/gcc/am335x/system_config/Debug/libsystem_config.a
LOAD /opt/CodeSourcery/arm-none-eabi/lib//libc.a
LOAD /opt/CodeSourcery/lib/gcc/arm-none-eabi/4.5.2//libgcc.a
OUTPUT(Debug/bbdidt.out elf32-littlearm)
.bss.pageTable 0x8008c000 0x4000
.bss.pageTable
0x8008c000 0x4000 Debug/enetLwip.o
.bss.ram 0x80090000 0x4
.bss.ram 0x80090000 0x4 Debug/lwiplib.o
There is clearly 'something'. There is another BSS section after the _bss_end, which contains a lot of stuff which is expected to be zeroed, but it is not zeroed, because zeroing finishes at address given by _bss_end.
The probable reason why this is done like this is, that the pageTable is statically declared and required to have 16kiB boundary address:
static volatile unsigned int pageTable[4*1024] __attribute__((aligned(16*1024)));
So as there is a gap between last linker declared BSS segment and pageTable, it places the _bss_end in the middle of the bss segment.
Now the question is, how to tell to linker (I'm using for this arm-none-eabi-ld) that _bss_end should be really at the end of BSS and not somewhere in the middle?
Many thanks
The fact that no statics are initialised makes me wonder: how have you come by your startup code? This code is required to perform the initialisations.
See http://doc.ironwoodlabs.com/arm-arm-none-eabi/html/getting-started/sec-cs3-startup.html - section 5.2.3 which says:
The C startup function is declared as follows:
void __cs3_start_c (void) __attribute__ ((noreturn));
This function performs the following steps:
Initialize all .data-like sections by copying their contents. For example, ROM-profile linker scripts use this mechanism to initialize writable data in RAM from the read-only data program image.
... etc
It sounds like you might be lacking that code.
thanks for all these comments. It was for me almost a detective work. At the end I have changed the linker script to get something like this:
SECTIONS
{
. = 0x80000000;
. = ALIGN(4);
.startcode :
{
*init.o (.text)
}
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.data :
{
*(.data)
}
. = ALIGN(4);
_bss_start = .;
.bss :
{
*(.bss)
*(COMMON)
*(.bss.*)
}
. = ALIGN(4);
_bss_end = .;
_stack = 0x87FFFFF8;
}
So I basically forced linker to include into BSS segment all the sub-segments which start with COMMON and .bss..
This apparently resolves the issue as the linker now generates correct map such, that it places _bss_end pointer really to the last address of BSS section.
So my soft now runs correctly, gets PHY running. I still cannot acquire the DHCP, but I guess this is a problem of uninitialised .data segment. LwIP uses at some places static assignments as
static u32_t xid = 0xABCD0000;
which is going into .data segment, but apparently it does not get initialised and hence I cannot get any DHCP anwer... but this is another story
thanks to all

Resources