Easy question for C people :).
Easy question for C people :).
Hi, I'm trying to write simple program in C for my OS and I need to compile it to BIN (binary) format. I prefer ASM but I understand that most of people would like to use C so I need to know how to do that.
hello.c :
void main (void)
{
asm ("movl $0xB80A0, %edi");
for (;;)
{
asm ("movl $' ', (%edi)");
asm ("movl $'2', (%edi)");
}
}
This is simple C program that will show all the time 2/space on the first place in second row on the screen. (He gets rights to write directly to screen area for tests only ).
I'm using DJGPP under WinXP, and I'm trying to compile it in such way:
D:\DJGPP\bin>gcc -c hello.c -o hello.o
hello.c: In function 'main':
hello.c:2: warning: return type of 'main' is not 'int'
D:\DJGPP\bin>ld --oformat binary hello.o -o hello.bin
d:/djgpp/bin/ld.exe: warning: cannot find entry symbol start; defaulting to 00001000
And when I'm putting created bin file to execute in my OS it crashes with GPF (0x13). So I understand that something is wrong with linking but I don't know how to compile it properly.
Please help .
EDIT:
Ok, I figured it out . Now I'm playing with C apps . No help neded, moderator can delete this topic .
hello.c :
void main (void)
{
asm ("movl $0xB80A0, %edi");
for (;;)
{
asm ("movl $' ', (%edi)");
asm ("movl $'2', (%edi)");
}
}
This is simple C program that will show all the time 2/space on the first place in second row on the screen. (He gets rights to write directly to screen area for tests only ).
I'm using DJGPP under WinXP, and I'm trying to compile it in such way:
D:\DJGPP\bin>gcc -c hello.c -o hello.o
hello.c: In function 'main':
hello.c:2: warning: return type of 'main' is not 'int'
D:\DJGPP\bin>ld --oformat binary hello.o -o hello.bin
d:/djgpp/bin/ld.exe: warning: cannot find entry symbol start; defaulting to 00001000
And when I'm putting created bin file to execute in my OS it crashes with GPF (0x13). So I understand that something is wrong with linking but I don't know how to compile it properly.
Please help .
EDIT:
Ok, I figured it out . Now I'm playing with C apps . No help neded, moderator can delete this topic .
Re: Easy question for C people :).
What did you do to fix it?? I have the same problem. I'm, trying to, use Windows XP/DJGPP as well to build my C++ kernel. When I boot the image in VMWare the virtual machine just keeps on rebooting.mrkaktus wrote: D:\DJGPP\bin>ld --oformat binary hello.o -o hello.bin
d:/djgpp/bin/ld.exe: warning: cannot find entry symbol start; defaulting to 00001000
Ok, I figured it out .
So the problem is in the linker script then?
My link.ld script looks like this:
Is it the address in the .text section (which in my script is set to 0x10000) which is wrong then? What determines what that address should be?
The linker script I'm using have been written from tutorials I've read. Are there any good resources where I can read more about what the different sections in the linker script actually means?
My link.ld script looks like this:
Code: Select all
OUTPUT_FORMAT("binary")
ENTRY(start)
SECTIONS
{
.text 0x10000:
{
code = .;
_code = .;
__code = .;
*(.text)
. = ALIGN(4096);
}
.rodata :
{
rodata = .;
_rodata = .;
__rodata = .;
*(.rodata)
. = ALIGN(4096);
}
.data :
{
data = .;
_data = .;
__data = .;
*(.data)
. = ALIGN(4096);
}
.bss :
{
bss = .;
_bss = .;
__bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .;
_end = .;
__end = .;
}
The linker script I'm using have been written from tutorials I've read. Are there any good resources where I can read more about what the different sections in the linker script actually means?
That does look ok, I don't align my sections on 4096 though, would make my kernel much larger than required. Anyways, where does your boot sector load your kernel at? Is 0x10000 a valid location, that looks ok to me, it will start running at 0x10000 though, so if you have data defined at the top, it will try running this rather than your code! Since you are outputing to a binary file, you don't even need to define start, because it will always start at that point in memory, make sure you have a jump as the first command, or any variables you define at the bottom. Not sure why it says defaulting to 00001000, if you are telling it 0x10000 that is off by a 0 . If that's a typo, then disregard, lol. These 2 locations should be identical though, if not something is wrong.
One last thing, if you are doing it in straight C, you would normally have an ASM stub program that would just jump to your c_main function, most compilers output a relocation table, which would tell whatever os that this is the entry point (pointer to main), so the os would automagically jump there. If you output to a binary file, it loses this table, and the pointer to your main function, so you must either a.) provide a way to store this poitner in a known location so you can jump to it, or B create a stub asm program that jumps to the main as the first instruction to make sure it doesn't try to run any variables![/img]
One last thing, if you are doing it in straight C, you would normally have an ASM stub program that would just jump to your c_main function, most compilers output a relocation table, which would tell whatever os that this is the entry point (pointer to main), so the os would automagically jump there. If you output to a binary file, it loses this table, and the pointer to your main function, so you must either a.) provide a way to store this poitner in a known location so you can jump to it, or B create a stub asm program that jumps to the main as the first instruction to make sure it doesn't try to run any variables![/img]
Provided that you're still using John Fines bootsector the start-up process should be as folllows:
cheers,
gaf
Having loaded your kernel the bootloader jumps to 0xFF800000 to start the operating system. In order to make variables and jumps function properly your kernel must be linked to the same address.John Fine wrote:01 ) Enables the A20 gate.
02 ) Switches to big real mode
03 ) Computes the locations of the FAT, the root directory and the first cluster.
04 ) Reads in the root directory
05 ) Scans the root directory for a specific file (KERNEL.BIN in the example)
06 ) Reads in the FAT
07 ) Reads the file (KERNEL.BIN) to RAM starting at physical 1Mb.
08 ) Builds two page tables and one page directory mapping:
a) The first 4Mb linear to the first 4Mb physical
b) Linear FF800000 (where all my protected mode images start) to physical RAM starting at 1Mb
c) Self map the page directory to the end of linear memory
09 ) Switch to protected mode with paging turned on
10 ) JMP to KERNEL.BIN at linear FF800000
cheers,
gaf
What do you do then?Ready4Dis wrote:That does look ok, I don't align my sections on 4096 though, would make my kernel much larger than required.
Ah, that's it! I'm using John Fine's bootf02. It loads a kernel from 0xFF800000. I changed the address in my link.ld script to this, and that worked! The warning still comes when i link, but now I am able to boot into the kernel.Ready4Dis wrote:Anyways, where does your boot sector load your kernel at?
Typo!Ready4Dis wrote:Not sure why it says defaulting to 00001000, if you are telling it 0x10000 that is off by a 0 . If that's a typo, then disregard, lol. These 2 locations should be identical though, if not something is wrong.
I'm writing (or at least trying) the kernel in C++. I have a loader.asm script like this:Ready4Dis wrote:One last thing, if you are doing it in straight C, you would normally have an ASM stub program that would just jump to your c_main function......
Code: Select all
[BITS 32] ;protected mode
[global start]
[extern _main]
start:
call _main
cli
hlt
Let me see if I'm understanding this correctly.
1. bootf02 is filling the first 512 bytes of my image file, and is ended by 55AA, and thus is interpreted as a bootable disk.
2. bootf02 does it's magic with A20, memory and such.
3. The bootloader jumps to 0xFF800000, which is where my code begins.
4. In my linker script I've stated that is the absolute beginning of my code.
5. In my loader.asm file I have a few definitions at the top, but start: is the first step run by my code. The first CPU instruction run by my code is "call _main".
6. During linking with ld, for some reason, it doesn't find the entry point called start, but since I'm using .text 0xFF800000 in my linker script it defaults to the correct address anyway.
Is it possible to use a tool to examine my .img file with a disassembler tool, and observe that "call _main" is located at 0xFF800000, or is this address only used during the actual booting process?
Hope you bear with me. I'm trying to wrap my head around this.
1. bootf02 is filling the first 512 bytes of my image file, and is ended by 55AA, and thus is interpreted as a bootable disk.
2. bootf02 does it's magic with A20, memory and such.
3. The bootloader jumps to 0xFF800000, which is where my code begins.
4. In my linker script I've stated that
Code: Select all
ENTRY(start)
5. In my loader.asm file I have a few definitions at the top, but start: is the first step run by my code. The first CPU instruction run by my code is "call _main".
6. During linking with ld, for some reason, it doesn't find the entry point called start, but since I'm using .text 0xFF800000 in my linker script it defaults to the correct address anyway.
Is it possible to use a tool to examine my .img file with a disassembler tool, and observe that "call _main" is located at 0xFF800000, or is this address only used during the actual booting process?
Hope you bear with me. I'm trying to wrap my head around this.
Hm, the boot loader is loaded at 0x7c00. If they thought, the boot loader must be loaded in 0x8c00, it would be executing there. You can tell the linker however, where the code would be running, so it could make the right addressing for that...I've debugger several programs with ollydbg and saw that the programs are using base address+offset or just address linked correctly...
So, if you load your code at 0xFF8000....it will run there. If not, then it won't run there. You must tell the linker the code will run at 0xFF80000...so it could create the right addresses.
For example, the file has several instructions:
Just an example. The compiler would generate (at least I think so) instructions like:
For example. Then you tell the linker, you want to set up the code for address 'x'. So, the linker will take the addresses and add x to them and generate the output code...
Sorry, if I'm not correct at these, but I'm not talking from experience, I'm talking from logic!
So, if you load your code at 0xFF8000....it will run there. If not, then it won't run there. You must tell the linker the code will run at 0xFF80000...so it could create the right addresses.
For example, the file has several instructions:
Code: Select all
mov si, wllcome
mov di, fuckoff
Code: Select all
mov si, 0x200
mov di, 0x105
Sorry, if I'm not correct at these, but I'm not talking from experience, I'm talking from logic!
I think, I have problems with Bochs. The biggest one: Bochs hates me!
Yes, use a hex editor, you should see a short or far jump (depending on how far the jump is, which depends on how much assembly and C end up between the jump and your main function). Also, I use no underscores (there is an option in GCC for this, makes it much simpler for me to remember variable names!) You have start set as global, so that is good, not sure why it can't find, it, but I had a similar problem, can't remember how I fixed it, can check my script when i go home tomorrow. It's not important, you can disregard it anyways, because once you compile to binary, it loses that type of information anyways, so it's not important . I don't align my segments on a 4096 boundary, the reason some people do this, is so they can keep data and code seperate, you can allocate data sections as read/write, and the code section as read only, this stops self modifying code from working, and can be a bit safer I guess, but I am not worried about it much, and my kernel needs to self modify itself, so not a big deal! Also, if you enable PAE, and are using 64k blocks, you would have to align on a 64k boundary, which means tons of wasted space for a smaller kernel/app. Also, unless you have a method to tell your boot loader how much of a BSS you require, put that section BEFORE the data section, this ensures that your BSS is actually in your binary flat file, so you don't have to allocate it after you load the kernel (unless you feel like it, that is fine, and if you have a very large BSS it'd be worth it to store it's size, and add that after the kernel is loaded from disk, and ZERO IT OUT!!). Anyways, hope this helps, let me know if you need more help, I have mine working pretty good and have a pretty good grasp on what's going on I think .Scalpel wrote:Let me see if I'm understanding this correctly.
1. bootf02 is filling the first 512 bytes of my image file, and is ended by 55AA, and thus is interpreted as a bootable disk.
2. bootf02 does it's magic with A20, memory and such.
3. The bootloader jumps to 0xFF800000, which is where my code begins.
4. In my linker script I've stated thatis the absolute beginning of my code.Code: Select all
ENTRY(start)
5. In my loader.asm file I have a few definitions at the top, but start: is the first step run by my code. The first CPU instruction run by my code is "call _main".
6. During linking with ld, for some reason, it doesn't find the entry point called start, but since I'm using .text 0xFF800000 in my linker script it defaults to the correct address anyway.
Is it possible to use a tool to examine my .img file with a disassembler tool, and observe that "call _main" is located at 0xFF800000, or is this address only used during the actual booting process?
Hope you bear with me. I'm trying to wrap my head around this.
With DJGPP you might have to add an underscore to your start label in loader.asmScalpel wrote:During linking with ld, for some reason, it doesn't find the entry point called start, but since I'm using .text 0xFF800000 in my linker script it defaults to the correct address anyway.
Code: Select all
[BITS 32] ;protected mode
[global _start]
[extern _main]
_start:
call _main
cli
hlt
That's the idea . The compiler creates symbols for variables and functions relative to the beginning of the executable. By setting an offset you tell the linker where the binary will be loaded, so that the addresses defined in the object files can be made absolute.INF1n1t wrote:Then you tell the linker, you want to set up the code for address 'x'. So, the linker will take the addresses and add x to them and generate the output code...
It's probably much easier to see the effect using a disassembler. GCC already comes with objdump, which should be sufficient to do the job:Ready4Dis wrote:Yes, use a hex editor, you should see a short or far jump.
Code: Select all
objdump -D --architecture=i386 --target=binary kernel.bin
cheers,
gaf