Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

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

stack

  • Please log in to reply
9 replies to this topic

#1 kuraga

kuraga

    CC Lurker

  • Just Joined
  • Pip
  • 3 posts

Posted 29 December 2010 - 03:16 AM

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 :)
  • 0

#2 mebob

mebob

    CC Devotee

  • Validating
  • PipPipPipPipPipPip
  • 467 posts
  • Programming Language:C, C++, Assembly
  • Learning:PHP

Posted 29 December 2010 - 10:05 AM

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?
  • 0
Latinamne loqueris?

#3 kuraga

kuraga

    CC Lurker

  • Just Joined
  • Pip
  • 3 posts

Posted 29 December 2010 - 11:11 AM

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?
  • 0

#4 RhetoricalRuvim

RhetoricalRuvim

    JavaScript Programmer

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1311 posts
  • Location:C:\Countries\US
  • Programming Language:C, Java, C++, PHP, Python, JavaScript

Posted 29 December 2010 - 12:26 PM

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.)
  • 0

#5 mebob

mebob

    CC Devotee

  • Validating
  • PipPipPipPipPipPip
  • 467 posts
  • Programming Language:C, C++, Assembly
  • Learning:PHP

Posted 29 December 2010 - 01:03 PM

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.
  • 0
Latinamne loqueris?

#6 kuraga

kuraga

    CC Lurker

  • Just Joined
  • Pip
  • 3 posts

Posted 29 December 2010 - 01:26 PM

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.

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?
  • 0

#7 mebob

mebob

    CC Devotee

  • Validating
  • PipPipPipPipPipPip
  • 467 posts
  • Programming Language:C, C++, Assembly
  • Learning:PHP

Posted 29 December 2010 - 05:39 PM

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.

  • 0
Latinamne loqueris?

#8 RhetoricalRuvim

RhetoricalRuvim

    JavaScript Programmer

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1311 posts
  • Location:C:\Countries\US
  • Programming Language:C, Java, C++, PHP, Python, JavaScript

Posted 29 December 2010 - 10:11 PM

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.
  • 0

#9 dargueta

dargueta

    I chown trolls.

  • Moderator
  • 4854 posts
  • Programming Language:C, Java, C++, PHP, Python, JavaScript, Perl, Assembly, Bash, Others
  • Learning:Objective-C

Posted 30 December 2010 - 12:04 AM

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.
  • 0

sudo rm -rf / && echo $'Sanitize your inputs!'


#10 RhetoricalRuvim

RhetoricalRuvim

    JavaScript Programmer

  • Expert Member
  • PipPipPipPipPipPipPip
  • 1311 posts
  • Location:C:\Countries\US
  • Programming Language:C, Java, C++, PHP, Python, JavaScript

Posted 30 December 2010 - 02:08 AM

Thanks for the help about segment registers. I didn't know how those worked until now.
  • 0





Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download