Jump to content

Stack problem in bootloader (it's written by myself)

- - - - -

  • Please log in to reply
9 replies to this topic

#1
kuraga

kuraga

    Newbie

  • Members
  • Pip
  • 3 posts
I wrote a bootloader (ASM) which loads kernel © in real mode. There is a problem with stack: if my kernel uses it, program doesn't work properly. For example, if a pointer (to a string) is passing to function via stack, the valuef of the string isn't right:

    __asm__(".code16gcc\n");  

    __asm__("jmp _start\n");


    int extern __attribute__((noinline)) __attribute__((regparm(3))) sprintf(char * buf, const char *fmt, ...)  

    {  

      __asm__ __volatile__ ("int 0x10" : : "a" (0x0E00 | *fmt), "b"(144));  

    }  


    void __attribute__((noreturn)) _start()  

    {  

      char buf[128];  

      sprintf(buf, "Sasha%d", 123);

      while(1);  

    } 


But if substitute '...' by 'int x' in args list, in Qemu 'S' symbol will be outputed.

There is the finish bootloader's fragment:

    mov   ax, SETUP_ADDR>>4   ; SETUP_SEG  

    mov   es, ax  

    mov   ds, ax  

    mov   cs, ax  

    mov   ss, ax  

    mov   sp, StackSeg ; at the end, after aidding bytes to 512  

    jmp   SETUP_ADDR>>4:0  

and compilation script:
    #!/bin/bash  

    gcc -c -Wall -save-temps -march=i386 -ffreestanding -Wno-main -fno-builtin -masm=intel -O0 -o kernel.o  kernel.c  

    ld -nostdlib -static -Ttext 0 --oformat binary -o kernel.bin kernel.o  

    nasm boot.asm -o boot.bsr  

and pure source.

Thanx and sorry, my English isn't so good :)

#2
mebob

mebob

    Programming Expert

  • Members
  • PipPipPipPipPipPip
  • 490 posts
What is your sprintf() function supposed to do? And what are you trying to accomplish by that bit of inline assembly in your sprintf() definition?
Latinamne loqueris?

#3
kuraga

kuraga

    Newbie

  • Members
  • Pip
  • 3 posts

mebob said:

What is your sprintf() function supposed to do? And what are you trying to accomplish by that bit of inline assembly in your sprintf() definition?
There is a minimum function, that demonstrates the problem. Now it prints a sybol fmt[0].

I understood a cool thing whan was seeing the assembler code was generated on compilation kernel.c:

	.file	"kernel.c"

	.intel_syntax noprefix

#APP

	.code16gcc


	jmp _start


#NO_APP

	.text

.globl sprintf

	.type	sprintf, @function

sprintf:

	push	ebp

	mov	ebp, esp

	push	ebx

	mov	eax, DWORD PTR [ebp+12]

	mov	al, BYTE PTR [eax]

	movsx	eax, al

	or	ah, 14

	mov	edx, 144

	mov	ebx, edx

#APP

# 6 "kernel.c" 1

	int 0x10

# 0 "" 2

#NO_APP

	pop	ebx

	leave

	ret

	.size	sprintf, .-sprintf

	.section	.rodata

.LC0:

	.string	"Sasha%d"

	.text

.globl _start

	.type	_start, @function

_start:

	push	ebp

	mov	ebp, esp

	add	esp, -128

	push	123

	push	OFFSET FLAT:.LC0

	lea	eax, [ebp-128]

	push	eax

	call	sprintf

	add	esp, 12

.L4:

	jmp	.L4

	.size	_start, .-_start

	.ident	"GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5"

	.section	.note.GNU-stack,"",@progbits


GCC doesn't initializes DS and SS registers!!! And what does "OFFSET SoMeThNgDaTa" means??? By why it doesn't? And how can I know the address of code segment? And where is it at all here?

#4
RhetoricalRuvim

RhetoricalRuvim

    JavaScript Programmer

  • Members
  • PipPipPipPipPipPipPipPip
  • 1,251 posts
  • Location:C:\Countries\US
I don't know if this would be helpful, but I decided to post this 'Building an OS' link:

Building an OS

(You asked something about the code segment, and I thought they talk something about those things in one of those tutorials on that page.)

#5
mebob

mebob

    Programming Expert

  • Members
  • PipPipPipPipPipPip
  • 490 posts
There isn't much initialization to do with DS, but the reason that it doesn't touch SS is because GCC is designed for protected mode, and expects the stack to be already set up, which it is when developing under an OS like Windows or Linux.

Your sprintf() function doesn't make any sense, you aren't using INT 10 correctly. For one, it doesn't work like printf(). And what is *buf for? You should try this in pure assembly first.
Latinamne loqueris?

#6
kuraga

kuraga

    Newbie

  • Members
  • Pip
  • 3 posts

mebob said:

Your sprintf() function doesn't make any sense, you aren't using INT 10 correctly. For one, it doesn't work like printf(). And what is *buf for? You should try this in pure assembly first.
No, no... This code *prints* symbol, if it's an ascii. There is another problem.

mebob said:

There isn't much initialization to do with DS, but the reason that it doesn't touch SS is because GCC is designed for protected mode, and expects the stack to be already set up, which it is when developing under an OS like Windows or Linux.
Okay. SS have to been set, yeah? And I don't understand about DS.
1) What does GCC think about DS? That it's point to what?
2) What does ASM think about DS when it is evaluating OFFSET .LC0? And what does LD? Remember about .code16gcc option...

And if GCC and ASM/LD thinks different, is there something to solve this?

#7
mebob

mebob

    Programming Expert

  • Members
  • PipPipPipPipPipPip
  • 490 posts
Actually, GCC and LD don't think about any segment registers at all. Protected mode only uses them as an index to the GDT, and programs don't edit the segment registers. They are set by the OS.
1. GCC assumes that DS is already set and doesn't use it for anything
2. DS is not used in calculating offsets.

And in case you don't know, the bootloader gets loaded to physical address 0x7c00 and gets executed in segment 0, so by default a bootloader has CS and DS as 0.

EDIT: I wasn't thinking when I typed part of this. Your bootloader gets started at segment 0 offset 0x7c00, which is the same as segment 0x07c0 offset 0, but CS is 0 when it gets started so it is easier to think of it as segment 0

Edited by mebob, 30 December 2010 - 04:36 AM.

Latinamne loqueris?

#8
RhetoricalRuvim

RhetoricalRuvim

    JavaScript Programmer

  • Members
  • PipPipPipPipPipPipPipPip
  • 1,251 posts
  • Location:C:\Countries\US
The Supernovah pages say that it might also be loaded into segment 0x7C00 and physical address 0, which I don't exactly get, but it said that it could be either that segment and address 0 or that address and segment 0. As an example, Supernovah used:

ORG 0x7C00 

jmp 0x0:start 

start: 

I don't exactly get the difference between 0x7C00:0x00 and 0x00:0x7C00, but those pages said some things about boot sectors and other things.

#9
dargueta

dargueta

    Writes binary right handed and hex left handed

  • Moderators
  • 4,705 posts
  • Programming Language:C, Java, C++, PHP, Python, Perl, Assembly, Bash, Others
  • Learning:JavaScript
Processors boot in 8086-emulating mode. Way back in the 70's when the 8086 was designed, memory was divided into segments, hence the segment registers CS, DS, ES, etc.

The 8086 calculated 20-bit addresses as (segment << 4) + offset instead of (segment << 16) + offset, so 07C0:0000 and 0000:7C00 evaluate to the same address.

Note that this means that segment 1 starts at byte 16 instead of byte 65536, so the same byte could have up to 4096 different segment-offset addresses!

So, as far as your initialization question: The BIOS always loads the first sector of the boot disk into memory at address 0000:7C00. Things you need to do:

1) Save DL somewhere. This is the drive ID from the drive you just booted from. You'll need it later.
2) Set all segment registers to the values you need. I always use 0.
3) Set SP to 0xFFFE; this is the highest value it can hold. If you put it lower you'll risk overwriting your code.

After that, enable A20, load your kernel, set up protected mode, and everything else you need to do.
sudo rm -rf /

#10
RhetoricalRuvim

RhetoricalRuvim

    JavaScript Programmer

  • Members
  • PipPipPipPipPipPipPipPip
  • 1,251 posts
  • Location:C:\Countries\US
Thanks for the help about segment registers. I didn't know how those worked until now.




1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users