Exercise

In this exercise, we study how user program is loaded into the process’ address space. It will be helpful to the implementation of the multi-programming project.

1   private boolean load(String name, String[] args) {
2   Lib.debug(dbgProcess, "UserProcess.load(\"" + name + "\")");
3  
4   OpenFile executable = ThreadedKernel.fileSystem.open(name, false);
5   if (executable == null) {
6   Lib.debug(dbgProcess, "\topen failed");
7   return false;
8   }
9  
10   try {
11   coff = new Coff(executable);
12   } catch (EOFException e) {
13   executable.close();
14   Lib.debug(dbgProcess, "\tcoff load failed");
15   return false;
16   }
17  
18   // make sure the sections are contiguous and start at page 0
19   numPages = 0;
20   for (int s = 0; s < coff.getNumSections(); s++) {
21   CoffSection section = coff.getSection(s);
22   if (section.getFirstVPN() != numPages) {
23   coff.close();
24   Lib.debug(dbgProcess, "\tfragmented executable");
25   return false;
26   }
27   numPages += section.getLength();
28   }
29  
30   // make sure the argv array will fit in one page
31   byte[][] argv = new byte[args.length][];
32   int argsSize = 0;
33   for (int i = 0; i < args.length; i++) {
34   argv[i] = args[i].getBytes();
35   // 4 bytes for argv[] pointer; then string plus one for null byte
36   argsSize += 4 + argv[i].length + 1;
37   }
38   if (argsSize > pageSize) {
39   coff.close();
40   Lib.debug(dbgProcess, "\targuments too long");
41   return false;
42   }
43  
44   // program counter initially points at the program entry point
45   initialPC = coff.getEntryPoint();
46  
47   // next comes the stack; stack pointer initially points to top of it
48   numPages += stackPages;
49   initialSP = numPages ⋆ pageSize;
50  
51   // and finally reserve 1 page for arguments
52   numPages++;
53  
54   if (!loadSections())
55   return false;
56  
57   // store arguments in last page
58   int entryOffset = (numPages - 1) ⋆ pageSize;
59   int stringOffset = entryOffset + args.length ⋆ 4;
60  
61   this.argc = args.length;
62   this.argv = entryOffset;
63  
64   for (int i = 0; i < argv.length; i++) {
65   byte[] stringOffsetBytes = Lib.bytesFromInt(stringOffset);
66   Lib.assert(writeVirtualMemory(entryOffset, stringOffsetBytes) == 4);
67   entryOffset += 4;
68   Lib.assert(writeVirtualMemory(stringOffset, argv[i]) == argv[i].length);
69   stringOffset += argv[i].length;
70   Lib.assert(writeVirtualMemory(stringOffset, new byte[] { 0 }) == 1);
71   stringOffset += 1;
72   }
73  
74   return true;
75   }

Recall that UserProcess.load() load the executable with the specified name into the user process. In Line 4 – 8, it first opens the executable from the stubFileSystem. Note that ThreadedKernel.fileSystem.open does not invoke any Nachos system calls, instead, it utilizes the file IO routines from Java.

The Coff constructor takes one argument, an OpenFile referring to the MIPS binary file. If there is any error parsing the headers of the specified binary, an EOFException is thrown. Note that if this constructor succeeds, the file belongs to the Coff object; it should not be closed or accessed anymore, except through Coff operations.

There are four Coff methods that operate on the Coff object:

The CoffSection class allows Nachos to access a single section within a COFF executable. Note that while the MIPS cross-compiler generates a variety of sections, the only important distinction to the Nachos kernel is that some sections are read-only (i.e. the program should never write to any byte in the section), while some sections are read-write (i.e. non-const data). There are four methods for accessing COFF sections:

Line 20 - 28 read each section of the COFF executable and ensures the sections do not overlap. Line 31 – 38 extract the arguments of the user programs and check if the argument array fits in one page. Line 44 sets the program pointer to point at the program entry point. Line 49 sets the stack point to the top of the stack. Line 54 – 55 load the COFF sections into memory.

Finally, the arguments are then stored at the last page in Line 57 – 72.

Questions:
  1. What do the variables this.argc and this.argv?
  2. In Line 64 – 72, how exactly are the arguments stored? [Hints: not all arguments are of the same length.]

Next, we run the Nachos in debug mode with config file in nachos/proj22 . The shell program is halt.coff, which is compiled from halt.c.

1  #include "syscall.h"
2  
3  int main()
4  {
5      halt();
6      /⋆ not reached ⋆/
7  }

We can see from the source code halt.c that it does nothing but invokes the system call halt(). Set an appropriate breakpoints in UserProcess and answer the following questions.

Questions
  1. How many sections are there in halt.coff? What are they?
  2. How many pages do the sections occupy?
  3. How many page does the stack occupy?
  4. What are the values of the initial PC and SP?
  5. Change the shell program to test/matmult.coff. Answer the above questions when Dim = 20 and Dim = 2000.