Page 1 of 2
C code global variables
Posted: Tue Jul 10, 2007 2:11 am
by spectrum
hi all,
i'm writing a very simple basic os, for test. Since i've started to write some basic c functions in protected mode, as my test kernel size is growed from 4K to 8K, i see that 2 global variables are no more initialized to 0. Only if i initialize them to 0 later, the value remain stored right.
Code: Select all
unsigned long cur_x = 0;
unsigned long cur_y = 0;
void _kmain ()
{
cur_x = 0; cur_y=0; // work only in this way.
if ((cur_y == 0) && (cur_x == 0))
{
for (;;);
}
}
Seems that the values, after the code has been loaded, are not initialized from anyone.
thanks, angelo
Posted: Tue Jul 10, 2007 2:26 am
by pcmattman
What compiler are you using, what file format are you using?
If GCC, can we see your link script?
Posted: Tue Jul 10, 2007 2:29 am
by spectrum
is gcc,
no link script, i've only a basic makefile for now
Code: Select all
C=gcc
BINARY=boot
OSDIR=$(CURDIR)
INCDIR=-I$(OSDIR)/include \
-I$(OSDIR)/../modules/include
SRCDIR=$(OSDIR)/src
MODDIR=$(OSDIR)/../modules/src
OBJDIR=$(OSDIR)/obj
# os sources
SRCS:=$(wildcard $(SRCDIR)/*.c)
OBJS:=$(patsubst %.c,%.o,$(SRCS))
OBJS:=$(patsubst $(SRCDIR)%,$(OBJDIR)%,$(OBJS))
# asms
ASMS:=$(wildcard $(SRCDIR)/*.S)
AOBJS:=$(patsubst %.S,%.o,$(ASMS))
AOBJS:=$(patsubst $(SRCDIR)%,$(OBJDIR)%,$(AOBJS))
# modules
MODS:=$(wildcard $(MODDIR)/*.c)
MOBJS:=$(patsubst %.c,%.o,$(MODS))
MOBJS:=$(patsubst $(MODDIR)%,$(OBJDIR)%,$(MOBJS))
$(BINARY): $(OBJS) $(MOBJS) $(AOBJS)
ld --oformat=binary -Ttext=7c00 $(AOBJS) $(OBJS) $(MOBJS) -o $@
$(OBJDIR)/%.o: $(MODDIR)/%.c
$(CC) $< $(INCDIR) -Wall -c -o $@
$(OBJDIR)/%.o: $(SRCDIR)/%.c
$(CC) $< $(INCDIR) -Wall -c -o $@
$(OBJDIR)/%.o: $(SRCDIR)/%.S
as $< -o $@
clean:
rm -f boot
rm -f obj/*.o
thanks
Posted: Tue Jul 10, 2007 2:34 am
by pcmattman
I'm assuming you're generating an ELF executable, and loading with GRUB...
You'll need a linker script. An example of one is shown here:
Code: Select all
ENTRY (_loader)
SECTIONS
{
. = 0x00100000;
.text :
{
*(.text)
*(.text.*)
}
.rodata ALIGN (0x1000) :
{
*(.rodata)
}
.data ALIGN (0x1000) :
{
start_ctors = .;
*(.ctor*)
end_ctors = .;
start_dtors = .;
*(.dtor*)
end_dtors = .;
*(.data)
}
.bss :
{
_sbss = .;
*(COMMON)
*(.bss)
_ebss = .;
}
end = .; _end = .; __end = .;
}
You do need a loader stub (typically written in assembly) to setup a stack, call main() and then loop forever when main() returns.
If you're not using ELF, I'm not quite as sure.
Posted: Tue Jul 10, 2007 2:39 am
by spectrum
The code is pure binary, not an ELF. I'm loading it with my own mbr. After entering PM, i've set up a stack to 0x20000, and it works.
Do i have to use the script anyway ?
and what does it mean . = 0x00100000; ?
thanks angelo
Posted: Tue Jul 10, 2007 2:48 am
by pcmattman
Basically the linker script sets up the entire binary to use certain memory locations, in this case to be linked to run at the 1 MB mark.
I use GRUB which loads my kernel to the 1 MB mark, hence the reason '.' (which is the start of the output file) is at 1 MB.
I'm not sure about a binary file, but I would make sure that all symbols are at the correct address (if you load your kernel to 0x00100000 without relocation, it won't read symbols properly).
What I think is happening is that
Code: Select all
unsigned long cur_x = 0;
unsigned long cur_y = 0;
is defined as '0x0', and '0x4'... Which would explain why you don't have 0 in them as you thought, because you would be reading from the real-mode IVT. Upon setting them to 0, you take out entry 0 of the IVT and replace it with 0.
Posted: Tue Jul 10, 2007 3:07 am
by spectrum
pcmattman,
many thanks,
with your script file, changing the start relocation to 7c00, global variables works for now!
angelo
Posted: Tue Jul 10, 2007 3:08 am
by pcmattman
I'd suggest switching to ELF and using GRUB as your loader. It's much safer and easier.
If you're on linux the build process is even easier - on Windows you'd have to build a cross-compiler.
Posted: Tue Jul 10, 2007 5:36 am
by JamesM
Spectrum,
pcmattman is totally correct in that it was probably the lack of a linker script that was the problem.
However not all of this script applies to you. All the section about *(.ctor*) and *(.dtor*) is C++ specific and is not required for C code.
Important to note is the .data section. Because you initialised your global variables, they go in here as opposed to the usual .bss segment. Originally, without a linker script, you didn't set the location of this segment, so what was possibly happening was that the .text (code) segment was getting too big and expanding into the .data area, overwriting your initial values.
Another possible explanation is that because you didn't specify the location of your .data segment, LD was placing it somewhere outside the 1MB mark, which will of course not be accessible until you set the A20 line.
Thought i'd mention this as it's always important to know WHY something went wrong, not just how to hack/fix it.
And I agree with pcmattman, if you're doing a kernel for funsies, skip the bootloader, it'll only make your life hell. If you are successful with your kernel, come back and make your own bootloader but its so nasty I would'nt reccomend it!
JamesM
Posted: Tue Jul 10, 2007 5:57 am
by spectrum
JamesM,
many thanks to have clarified the link script meaning.
Gate A20 has been enabled.
I'm developing this little kernel for a job research purpose, we need it as indipendent os, so i've also written the mbr, the loader, a little file system, and now im into the interrupt routines stuff.
Thanks all again
angelo
Posted: Tue Jul 10, 2007 2:04 pm
by df
uninitialised global vars go in the bss section, which you need to zero yourself on load. since its not actually part of the image.
Posted: Tue Jul 10, 2007 5:48 pm
by pcmattman
JamesM wrote:However not all of this script applies to you. All the section about *(.ctor*) and *(.dtor*) is C++ specific and is not required for C code.
My kernel is C++, and I didn't think of removing them...
Posted: Wed Jul 11, 2007 12:49 am
by JamesM
JamesM wrote:Because you initialised your global variables, they go in here as opposed to the usual .bss segment.
@df:
Any initialisation of global vars (even if it is only to zero) forces them into the .data section.
Posted: Wed Jul 11, 2007 10:59 am
by frank
JamesM wrote:JamesM wrote:Because you initialised your global variables, they go in here as opposed to the usual .bss segment.
@df:
Any initialisation of global vars (even if it is only to zero) forces them into the .data section.
Not with my compiler. All of my zero initialized globals go into the .bss section. I am using a elf cross-compiler with gcc 4.1.2 and ld 2.17.
Posted: Wed Jul 11, 2007 12:42 pm
by Combuster
The same holds for my 3.4.4 gcc. Its just a valid optimisation as the bss is to be zeroed anyway and you don't need to store those 0's in the data section.
Also for completeness sake, C++ isn't the only language using ctors/dtors sections. I need it for freebasic as well, and several other languages might use it as well.