|[3 earlier articles]|
|Re: C 16 bit compiler firstname.lastname@example.org (BGB / cr88192) (2010-09-28)|
|Re: C 16 bit compiler email@example.com (George Neuner) (2010-09-29)|
|Re: C 16 bit compiler ArarghMail009@Arargh.com (2010-09-30)|
|Re: C 16 bit compiler firstname.lastname@example.org (Jack Smith) (2010-10-01)|
|Re: C 16 bit compiler email@example.com (George Neuner) (2010-10-01)|
|Re: C 16 bit compiler firstname.lastname@example.org (George Neuner) (2010-10-03)|
|Re: C 16 bit compiler email@example.com (BGB / cr88192) (2010-10-06)|
|Re: C 16 bit compiler firstname.lastname@example.org (Walter Bright) (2010-11-11)|
|Re: C 16 bit compiler email@example.com (Marco) (2010-11-20)|
|From:||"BGB / cr88192" <firstname.lastname@example.org>|
|Date:||Wed, 6 Oct 2010 11:29:43 -0700|
|References:||10-09-027 10-09-033 10-10-002 10-10-006|
|Keywords:||C, code, architecture, comment|
|Posted-Date:||06 Oct 2010 16:49:32 EDT|
"George Neuner" <email@example.com> wrote in message
> On Thu, 30 Sep 2010 03:42:04 -0500, ArarghMail009@Arargh.com wrote:
>>On Wed, 29 Sep 2010 16:31:09 -0400, George Neuner
>>>A first stage boot loader can only be up to one device block long.
>>Not really. While the BIOS or partition table loader will only load 1
>>block (of usually 512 bytes), there is absolutely no reason that the
>>first block can't just go and load as many more blocks as it wants to.
>>I have done that, usually in the partition table loader.
> Yes, the boot block can load additional blocks - and usually does
> because the OS/program startup loader is typically linked into the
> same executable. But you can't use a meta-executable format like ELF
> - it needs to be a flat, non-segmented format.
granted, this whole matter may be more topical to alt.os.development or
similar, but oh well...
there may be restrictions depending on the filesystem in use as well.
for example, many filesystems by default only provide some space in the
first sector (512 minus BPB FAT/NTFS), or maybe 1kB (EXT2/...), in which to
fit the entire bootloader.
hence, usually then, the first thing one has to do is get the rest of the
loader loaded, which is usually by reading it in from the filesystem.
granted, yes, it is possible that one could lay it out in memory as a linear
chunk (say, the first 512 bytes are simply the bootloader, and everything
after is the first/second stage, so the goal is to locate this additional
code and load it directly after the bootloader).
back when I did this though, I did the bootloader and second stage as
separate components though.
even in the MBR, newer bootloaders/bootmanagers may have to contend with
GUID partition tables or similar, rather than use the old trick of simply
placing their data directly after the MBR sector (although, it can still
work, as 'track 0' is a fairly large space and only a small amount is likely
needed for GPT, unless of course the partitioner no longer honors the "begin
and end partitions on a track" tradition).
then again, usually where one finds GPT one also finds EFI, so it may not
actually matter that much.
>>>Because of this it would usually be written in assembler. It's
>>>possible to write one in C, but you have to be *EXTREMELY* careful
>>>about using any library functions. Still it would be difficult to get
>>>a useful C program to fit into a 512 byte floppy sector.
>>Very true. :-)
> I want to make sure there's no confusion due to different people
> numbering the boot stages differently. I order them based on disk
> device boot as Unix does, from zero as :
> 0 : device boot block
> 1 : file system boot block
> 2 : program/OS kernel
> For memory devices like Flash and SSD, there may be no file system and
> all the bootstrap work is done from the device boot block. In that
> case, stages 0/1 are synonymous.
> Regardless, if there is a file system, it can't be used until, at
> least, stage 2. Stage 0/1 essentially are limited to BIOS functions
> and can reference only raw device blocks. That, along with block size
> limitations on many devices, is the reason that these stages are
> normally written in assembler.
I was using the notion:
first-stage=bootloader (from filesystem bootsector);
second-stage=OS loader (comes from filesystem, usual goal is to load/setup
the kernel itself).
partly agreed, however, most bootloaders I have seen usually have used the
filesystem to get the kernel loaded (in addition to the secondary loader).
typically though these files are placed in the root directory (commonly,
only the first entries of the root directory may be visible at this point,
and possibly files may be limited to being located in contiguous sectors or
similar, such as to save having to have full logic for dealing with the FAT
or cluster-spans or whatever...).
say, the boot loader looks for the files "LOADER.SYS" and "KERNEL.EXE",
loads them into their initial spots in memory, and jumps to "LOADER.SYS",
which may in turn do other things (in some order...):
start setting up pmode (usually first going into big-real/unreal mode);
copying the kernel to its load address (say, ImageBase in the PE/COFF
extended header or similar);
maybe load in any other relevant bootup files (say, any drivers from a list
of known drivers, ...);
enter pmode (or maybe now longmode) and jump to the kernels' entry point (or
one could set up the stack in the second stage and call into the kernel,
possibly allowing the kernel to 'return' to real mode say to reboot or
whatever, but I am not aware of anyone doing this...).
also reasonable is to only load in the secondary loader by the bootsector,
in which case a directory (say, here called simply SYSTEMROOT) is used to
find, say, "KERNEL.EXE", "BOOTLST.INF", and possibly any secondary modules
(although a multi-module kernel could somewhat complicate the loader, as
then one would need to deal with things like import/export-resolution, image
it should probably all be fairly similar if one uses ELF or MachO or similar
for the kernel as well?...
> It is perfectly possible to write block loaders in C, but there's
> little point to doing so because they are so simple and because few
> (if any) C compilers have any BIOS specific functions - usually just
> some generic API for invoking software interrupts/traps ... meaning
> that you are writing what is effectively assembler anyway and, in
> addition, working without access to most of the C standard library.
agreed, and this is probably the main relevant point:
C makes the thing bigger, and really "brings nothing to the table" over
using just ASM for the task.
even for the main OS loader, which arguably does a good deal more, one is
often still better off using ASM.
reasons would include:
there is no OS available, so no real C library either;
a lot of logic may need to operate in mixed CPU modes, which are not
generally supported by C compilers (I doubt any C compilers are written
specifically for unreal mode);
still lots of interfacing with the BIOS;
at first, back when I was doing OS stuff, I first thought I also wanted to
use C for the second stage loader, but really, it worked out perfectly well
writing it all in ASM.
for a much bigger and more complex second-stage, using some C could make
sense, but I am not sure what would be done which would really need it (and
also, little is likely to be done which is actually independent of the
underlying HW, so portable code in a loader is not likely a real goal).
[This is getting rather far afield from compilers, so I'm declaring it
over. Feel free to move it to comp.lang.c for C specific issues or
an OS or machine group. -John]
Return to the
Search the comp.compilers archives again.