OSDev.org

The Place to Start for Operating System Developers
It is currently Tue Mar 19, 2024 1:57 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 10 posts ] 
Author Message
 Post subject: MSVC Grub - A new way
PostPosted: Sun Apr 18, 2010 12:20 am 
Offline
Member
Member
User avatar

Joined: Sun Feb 18, 2007 7:28 pm
Posts: 1564
Hello everyone,

Ive been working on experimenting with booting GRUB using Microsoft Visual C++ 2008 and developed an easy method of allowing one to boot a PE kernel built with MSVC with GRUB. The method seems too easy to warrant a tutorial on it, so I decided to create a post describing the method in hopes that it will be helpful for other developers using MSVC.

To get it booting, all we need to do is define a multiboot structure. Here is the declaration:
Code:
#pragma pack (push, 1)

/**
*   Multiboot structure
*/
typedef struct _MULTIBOOT_INFO {

   uint32_t magic;
   uint32_t flags;
   uint32_t checksum;
   uint32_t headerAddr;
   uint32_t loadAddr;
   uint32_t loadEndAddr;
   uint32_t bssEndAddr;
   uint32_t entryPoint;
   uint32_t modType;
   uint32_t width;
   uint32_t height;
   uint32_t depth;

}MULTIBOOT_INFO, *PMULTIBOOT_INFO;

#pragma pack(pop,1)
Easy enough :) With our structure declared, we just define the multiboot structure in the beginning of the .text section. The .text section is always the first section of the image after the PE header structures. PE data (such as resources and export/import tables etc) are always at the end of the binary so the PE headers are always the same size (0x400). The multiboot spec says that this structure must be defined within the first 8k in the image, aligned on a 32-bit boundary. By placing this structure at the beginning of a segment in the file, it will always be at 0x400 aligned to the set section alignment, which are always 32 bit aligned:

Code:
#pragma section(".text")
__declspec(allocate(".text"))
MULTIBOOT_INFO _MultibootInfo = {

   MULTIBOOT_HEADER_MAGIC,
   MULTIBOOT_HEADER_FLAGS,
   CHECKSUM,
   HEADER_ADDRESS,
   LOADBASE,
   0, //load end address
   0, //bss end address
   KeStartup
};

Thats all that there is to it. KeStartup is your entry point function, LOADBASE is the base address of your kernel (like 1MB for example), HEADER_ADDRESS is the address of the multiboot header (which happens to be LOADBASE+0x400 do to .text always starting at 0x400), magic is 0x1BADB002, tested flags of 0x00010003 and the checksum being -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS).

Here is the complete example:
Code:
#pragma pack (push, 1)

/**
*   Multiboot structure
*/
typedef struct _MULTIBOOT_INFO {

   uint32_t magic;
   uint32_t flags;
   uint32_t checksum;
   uint32_t headerAddr;
   uint32_t loadAddr;
   uint32_t loadEndAddr;
   uint32_t bssEndAddr;
   uint32_t entryPoint;
   uint32_t modType;
   uint32_t width;
   uint32_t height;
   uint32_t depth;

}MULTIBOOT_INFO, *PMULTIBOOT_INFO;

#pragma pack(pop,1)

/**
*   Kernel entry
*/
void KeStartup ( PMULTIBOOT_INFO* loaderBlock ) {

   __halt ();
}

//! loading address
#define LOADBASE                     0x100000

//! header offset will always be this
#define   ALIGN                           0x400
#define   HEADER_ADDRESS         LOADBASE+ALIGN

#define MULTIBOOT_HEADER_MAGIC        0x1BADB002
#define MULTIBOOT_HEADER_FLAGS        0x00010003
#define STACK_SIZE                    0x4000   
#define CHECKSUM                      -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)

#pragma section(".text")
__declspec(allocate(".text"))
MULTIBOOT_INFO _MultibootInfo = {

   MULTIBOOT_HEADER_MAGIC,
   MULTIBOOT_HEADER_FLAGS,
   CHECKSUM,
   HEADER_ADDRESS,
   LOADBASE,
   0, //load end address
   0, //bss end address
   KeStartup
};
Thats all that there is to it. Assuming GRUB is configured to boot your kernel, this should make your kernel bootable by GRUB. Tested and works with two separate projects. Please post comments if there are any :D

*Wiki: I never used the Wiki so dont know how to add pages. Please feel free to use this content if you like.

_________________
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}


Top
 Profile  
 
 Post subject: Re: MSVC Grub - A new way
PostPosted: Sun Apr 18, 2010 1:25 am 
Offline
Member
Member

Joined: Fri Jul 11, 2008 5:15 am
Posts: 342
Location: Hungary
Nice work there. There is only one small problem though. Putting the multiboot header in the .text section does not prevent the linker from arbitrarily moving it within the section (especially if you turn on "smart linking" - when every function gets compiled into a separate .text section, and the linker merges them together). A better way would be using linker scripts ... wait, those are not available with msvc :)

But have no fear, here is another way. You may need to tweak this a little bit.

Code:
#pragma section(push, ".text$1")
MULTIBOOT_INFO _MultibootInfo =
{
   ...
};
#pragma section(pop)


The linker always groups together these $1 ($2 etc.) sections with the main section (text in this case), but puts these ordered sections before it. This is how the MS C/C++ runtime implements static constructors/destructors (by putting them into these ordered sections).

Disclaimer: I am writing this entirely from memory. It may be incorrect in some places.


Top
 Profile  
 
 Post subject: Re: MSVC Grub - A new way
PostPosted: Sun Apr 18, 2010 9:31 am 
Offline
Member
Member
User avatar

Joined: Sat Nov 29, 2008 1:07 pm
Posts: 550
Location: Throw a dart at central Texas
You could do what I was thinking about doing and replace the DOS stub with a tiny multiboot program, which contains the DOS MZ header, then the multiboot header, then some p-mode code to look through the PE header to find the entry point and run that. Since the DOS stub is guaranteed to be at the very beginning of the file, and the linker won't reorganize it, the multiboot structure will be within the first 8k of the file. If you want to, you could do: DOS header, RMode-code that goes to PMode and relocates the exe to 1MB and jumps to the multiboot bit, then the multiboot header, then the multiboot code which finds and runs the entry point of the PE part of the exe. Just be sure that the RM-code sets ebx (I think it was) to a different code than GRUB so your program doesn't get confused.

If you really wanted to, you could have the multiboot stub do all of the relocations and whatnot first, then run the entry point.

_________________
Owner of Fawkes Software.
Wierd Al wrote:
You think your Commodore 64 is really neato,
What kind of chip you got in there, a Dorito?


Top
 Profile  
 
 Post subject: Re: MSVC Grub - A new way
PostPosted: Mon Apr 19, 2010 10:02 am 
Offline
Member
Member
User avatar

Joined: Sat Dec 27, 2008 2:34 pm
Posts: 548
Location: Belgium
Looks like your experiments brought you to an interesting discovery. It's always nice to discover something new just by experimenting, certainly in the OSDev world. Great job!

_________________
When the chance of succeeding is 99%, there is still a 50% chance of that success happening.


Top
 Profile  
 
 Post subject: Re: MSVC Grub - A new way
PostPosted: Mon Apr 19, 2010 12:19 pm 
Offline

Joined: Fri Feb 26, 2010 10:44 am
Posts: 8
This is great now i can make the boot process in my project make sense :-). just one question, my current method of booting through grub has an annoying limit on the number of characters stored in memory. i have been able to increase it significantly, but its still there and it bothers me. is there a limit in this method as well?

_________________
My Blog:
http://clwillingham.wordpress.com/

My Project:
the OSDK


Top
 Profile  
 
 Post subject: Re: MSVC Grub - A new way
PostPosted: Tue Apr 20, 2010 7:53 pm 
Offline
Member
Member
User avatar

Joined: Sun Feb 18, 2007 7:28 pm
Posts: 1564
Hello,

It seems this method may not work in large projects. While defining the structure in its own section will guarantee alignment, there is no way of knowing if the section will be in the first 8k of the binary needed by the multiboot standard.

ru2aqare's method may not work - while .text$0 should be placed first in .text MSVC doesnt treat it that way, it seems to place it in its own section possibly away from .text. I personally cannot find a way to create a section before .text. Thus MSVC may very well define the data outside the 8k mark.

Firestryke31's method, however, would provide a solution here. If we use a stub program that defines the structure in it and uses its entry point to be called by the bootloader, which in turn, calls the kernels real entry point, should work very well. - Great idea :D

*edit: hm... Actually Firestryke31's method wont work. The stub program will only link if its a 16 bit program. Multiboot spec says that, when execution is transfered, the machine must already be running in protected mode.

clwillingham- Sorry, I am not sure what it is that you mean. Please rephrase.

Creature- Thanks :D Its always fun experimenting with different things and creating your own solutions :D

*edit2: This seems to fix it:
Code:
#pragma code_seg(".a$0")
__declspec(allocate(".a$0"))
MULTIBOOT_INFO _MultibootInfo = {

   MULTIBOOT_HEADER_MAGIC,
   MULTIBOOT_HEADER_FLAGS,
   CHECKSUM,
   HEADER_ADDRESS,
   LOADBASE,
   0, //load end address
   0, //bss end address
   KeStartup
};

#pragma comment(linker, "/merge:.text=.a")
The above modification has been tested and boots with Grub. On both machines with two different MSVC kernels it creates the structure always at the beginning of the file, linking the section before the .text section in the program binary.

Please feel free to make comments if there are any :)

_________________
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}


Top
 Profile  
 
 Post subject: Re: MSVC Grub - A new way
PostPosted: Tue Apr 20, 2010 10:40 pm 
Offline
Member
Member

Joined: Thu Jan 14, 2010 5:35 pm
Posts: 31
Don't forget that you need to link with an "/align:512" or similar option that guaranties that section alignment in the file matches virtual addresses that linker assumes.


Top
 Profile  
 
 Post subject: Re: MSVC Grub - A new way
PostPosted: Wed Apr 21, 2010 9:06 am 
Offline
Member
Member
User avatar

Joined: Sat Nov 29, 2008 1:07 pm
Posts: 550
Location: Throw a dart at central Texas
neon wrote:
*edit: hm... Actually Firestryke31's method wont work. The stub program will only link if its a 16 bit program. Multiboot spec says that, when execution is transfered, the machine must already be running in protected mode.


MSVC has the /stub option, where the only thing it does is update the field in the MZ header that points to the PE header. There is nothing saying you can't have the stub divided into 3 parts: a 16 bit part that sets up protected mode and goes to the common p-mode part (for if the user for some reason started the program in DOS or Dex's bootloader or something like that), the GRUB p-mode part that contains the multiboot header and the multiboot entry point which jumps to the next part, and finally the common p-mode part which looks in the PE header and jumps to the entry point there. There's no reason why the multiboot entry point has to be the same as the MZ entry point or the PE entry point.

Basically the EXE would look something like this:

Code:
MZ Stub:
- 16-bit R-mode code (run by MZ loader like DOS)
-- Sets up basic protected mode
-- Go to PMcommon

- 32-bit Multiboot code
-- Multiboot header with entry point set to MBentry
MBentry:
-- does some data massaging
-- Go to PMcommon

- Common P-mode code
PMcommon:
-- Sets up desired environment (paging, GDT, etc.)
-- Looks through PE header to find real entry point
-- Optional: Performs base relocations
-- Runs PE entry point

PE data and whatnot:
- Contains the bulk of the program

_________________
Owner of Fawkes Software.
Wierd Al wrote:
You think your Commodore 64 is really neato,
What kind of chip you got in there, a Dorito?


Top
 Profile  
 
 Post subject: Re: MSVC Grub - A new way
PostPosted: Sun Jun 06, 2010 6:30 pm 
Offline
Member
Member
User avatar

Joined: Sat Apr 21, 2007 7:21 pm
Posts: 127
Awesome!! Thanks so much for this!

I am going to add this to the Visual c++ wiki entry

Edit: Nvm, I don't have access...

_________________
Mouse Pad - Coming in the distant future...
Kernel: Indigo Kernel - v0.0.1 [b]

Thanks to JamesM and BrokenThorn for there tutorials!


Top
 Profile  
 
 Post subject: Re: MSVC Grub - A new way
PostPosted: Mon Jun 07, 2010 4:16 am 
Offline
Member
Member
User avatar

Joined: Sat Dec 27, 2008 2:34 pm
Posts: 548
Location: Belgium
astrocrep wrote:
Awesome!! Thanks so much for this!

I am going to add this to the Visual c++ wiki entry

Edit: Nvm, I don't have access...


You do.

_________________
When the chance of succeeding is 99%, there is still a 50% chance of that success happening.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 10 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: No registered users and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group