1

I have been analyzing the code of bootmain.c code in xv6 kernel:

void
bootmain(void)
{
  struct elfhdr *elf;
  struct proghdr *ph, *eph;
  void (*entry)(void);
  uchar* pa;

  elf = (struct elfhdr*)0x10000;  // scratch space

  // Read 1st page off disk
  readseg((uchar*)elf, 4096, 0);

  // Is this an ELF executable?
  if(elf->magic != ELF_MAGIC)
    return;  // let bootasm.S handle error

  // Load each program segment (ignores ph flags).
  ph = (struct proghdr*)((uchar*)elf + elf->phoff);
  eph = ph + elf->phnum;
  for(; ph < eph; ph++){
    pa = (uchar*)ph->paddr;
    readseg(pa, ph->filesz, ph->off);
    if(ph->memsz > ph->filesz)
      stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz);
  }

  // Call the entry point from the ELF header.
  // Does not return!
  entry = (void(*)(void))(elf->entry);
  entry();
}

I understand that the following line

readseg((uchar*)elf, 4096, 0);

is trying to copy elf header from the disk to the memory at elf address, but I don't understand why is it copying 4kb where the size of the elf header itself is 52 bytes.

After running readelf -h kernel, I get the following info about elf headers:

Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         3

which would mean (if my math serves me well) that elf header + program header table doesn't take more than 148 bytes.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198

1 Answers1

2

The code you are showing is the C code for the xv6 bootloader.

The number of program headers isn't known until it at least reads the ELF header. Rather than read from disk twice (once for the ELF header and then once for the program headers) they just read the entire first page (4KiB). They are making the assumption that the combined program header and ELF header don't exceed 4 KiB.

It would have been preferable to load the ELF header; determine the size of the program headers and read them into memory; then read in segments associated with each program header. This code would break if the ELF header and all the other headers somehow exceeded 4 KiB.

After reading the code and some of the xv6 documentation it appears they simplified loading the kernel ELF file so that the code that was generated could fit in a 512 byte boot sector. It was likely a design decision to read the data once, but in all honesty they probably weren't expecting the kernel itself to have a large number of headers. Just reading the first 4KiB was convenient and simple.

Of interest might be the xv6 documentation on page 102 where it describes the fact that simplifications had to be done to fit the bootloader code (and the ELF loader) into a 512 byte boot sector:

The boot loader described in this appendix compiles to around 470 bytes of machine code, depending on the optimizations used when compiling the C code. In order to fit in that small amount of space, the xv6 boot loader makes a major simplifying assumption, that the kernel has been written to the boot disk contiguously starting at sector 1. More commonly, kernels are stored in ordinary file systems, where they may not be contiguous, or are loaded over a network. These complications require the boot loader to be able to drive a variety of disk and network controllers and understand various file systems and network protocols. In other words, the boot loader itself must be a small operating system. Since such complicated boot loaders certainly won’t fit in 512 bytes, most PC operating systems use a two-step boot process. First, a simple boot loader like the one in this appendix loads a full-featured boot-loader from a known disk location, often relying on the less space-constrained BIOS for disk access rather than trying to drive the disk itself. Then the full loader, relieved of the 512-byte limit, can implement the complexity needed to locate, load, and execute the desired kernel.

I think one can conclude that among the simplifying assumptions are the ones regarding the design of the ELF loader in the bootloader.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198