Skip to content

Assignment 1

Pavel I. Kryukov edited this page Oct 6, 2018 · 7 revisions
** Note: this assignment is finished.**

Introduction

In this assignment you will implement the main interfaces of the functional memory. They include initialization from a MIPS executable file in the ELF format, reading/writing data and others.

For this assignment you should create a fork of our repo as it is described in this wiki page and work only there.

Notes

  • You should work in forked repository. See Git&GitHub cheat sheet for instructions.
  • In forked repository create branch task_1: git branch task_1. See Pro Git about branching in Git
  • Switch to your new branch with git checkout task_1 command. Your Assignment 1 should be done in this branch.
  • Use assert macro from assert.h to handle wrong parameters and to protect your code from hidden errors. For more information about assert usage visit this page.
  • You have to follow our code guidelines. Don't violate them!
  • Set tabs as 4 spaces in your text editor.
Don't send pull requests to main repository - just contact project mentors via e-mail when you finish task

ELF Parser

The class ElfSection implements functionality to parse a MIPS executable file, download and store content of all their sections. The class is defined in trunk/func_sim/elf_parser/elf_parser.h.

Note: the ELF parser has already implemented. You don't need to change anything in it.

Download contents of an ELF section

Functionality for parsing ELF executable files of the MIPS architecture is already implemented. To create a vector of objects of ElfSection class that contain the data of all the section in the binary, you have to invoke the following static function:

static void getAllElfSections( const char* elf_file_name,
                               vector<ElfSection>& sections_array /*used as output*/);

with the following parameters:

  • elf_file_name - name of a MIPS executable file (e.g. mips_bin_exmpl.out). You can find examples of such files in trunk/tests/samples/ or even create your own files (for more information see README.txt in that folder).
  • sections_array - STL vector that is used as output container (don't worry you will be able to complete this task even if you don't know STL).

You can see how it works in trunk/func_sim/elf_parser/main.cpp. This file implements elf_parser program that downloads and prints all the sections from the executable file given as the only parameter (e.g. ./elf_parser mips_bin_exmpl.out). The output will be something like the following:

Dump ELF section ".reginfo"
  size = 24 Bytes
  start_addr = 0x400094
  Content:
    0x400094:    000c0000
  ....
    0x4000a8:    70814100

Dump ELF section ".text"
  size = 16 Bytes
  start_addr = 0x4000b0
  Content:
    0x4000b0:    41000b3c
    0x4000b4:    cc006b25
    0x4000b8:    04006a8d
  ....

Dump ELF section ".data"
  size = 192 Bytes
  start_addr = 0x4100c0
  Content:
    0x4100c0:    00010203
    0x4100c4:    04050607
    0x4100c8:    08090000
    0x4100cc:    07000000
    0x4100d0:    0b000000
    0x4100d4:    0d000000
  ....

To build elf_parser just type make in trunk/func_sim/elf_parser/.

Compare the output of this program with the data printed by the standard mips-objdump (mips-objdump -D mips_bin_exmpl.out). You can see that each byte has the same value, but bytes in a words are mirrored. It occurs, because mips-objdump prints each word as a value of 4 Bytes from left to right (the least significant byte on the left), but elf_parser prints each word per byte from right to the left.


Functional Memory

The functional memory is an abstraction that present how the memory is defined in the ISA. In the other word, it is how a programmer sees the memory. Note that functional memory does not reflect the organization of the memory in the real machine.

In short, the functional memory is a sequence of bytes. Each of them has unique address and can be read and written using it.

The FuncMemory class implements a functional memory model. It provides interfaces to parse an ELF formatted executable file, download and store content of ELF sections, read and write data. The class is defined in trunk/func_sim/func_memory/func_memory.h.

Note: all interfaces of the FuncMemory class are empty. You have to implement them.

Hierarchy of Functional Memory

The typical address size for the MIPS architecture is equal to 32 bits, i.e. the memory size is 2^32 = 4 GB. It is clear that such amount of memory cannot be assigned in the simulator. Moreover, an usual program uses significantly less memory.

In order to not allocate the whole required bytes at once, but still be able to read or write by any address, the functional memory is organized as a hierarchy.

Note: that is done only because of the limitation of our program model and does not reflect the real memory organization.

There are three levels in our implementation of the functional memory:

  • The lowest level is a page. It is a block of bytes that keeps the real data.
  • The middle level is a set of pages. It is an array of pointers to all pages that includes in the set.
  • The highest level is an array of sets.

The hierarchy is shown on the following picture:

NULL means that this set or page has not been written yet and the memory for it is not allocated in the simulator.

The values of set number, page number and offset can be extracted directly from the memory address. The following picture shows the address separating:

Simplified Hierarchy of Functional Memory

If the three level hierarchy is too complex for you, it is possible to implement a simpler hierarchy described in this section. Note that in this case you will receive lower mark, but it is better than nothing.

In the simplified version the functional memory is just an static array of bytes (i.e. no dynamic memory allocation). In this case the size of your memory will be small and can be able to process only a limited range of memory address. For our work, it will be enough to process only addresses from the following range: [0x3FFFF0; 41FFF0]. Therefore, the array will contain 0x20000 bytes. As it is not a very larger number, it can be allocated at once statically.

Note: if you decide to choose this option you should not care about address separation into set, page and offset.

Download contents of ELF sections

Note: the constructor of the FuncMemory class is empty and to implement them is a part of the first assignment. Use ElfParser class to do that.

An object of FuncMemory class is initialized taking the information from the binary ELF file. To create an object of FuncMemory class and download context of ELF sections, you have to invoke its constructor:

FuncMemory ( const char* executable_file_name,
             uint64 addr_size = 32,
             uint64 page_num_size = 10,
             uint64 offset_size = 12);

with the following parameters:

  • elf_file_name - name of a MIPS executable file (e.g. mips_bin_exmpl.out). You can find examples of such files in trunk/tests/samples/ or even create your own files (for more information see README.txt in that folder).
  • addr_size - the size of the memory address (number of bits).
  • page_num_size - the number of bits in the memory address that represent the page number inside the set.
  • offset_size - the number of bits in the memory address that represent the byte number inside the page.

You can see how it should work in trunk/func_sim/func_memory/main.cpp. This file implements func_memory program that downloads sections from an executable file given as the only parameter (e.g. ./elf_parser mips_bin_exmpl.out) and prints the contents of the functional memory. To build func_memory just type make in trunk/func_sim/func_memory/.


Read and write data

Note: the read interfaces are empty and to implement them is a part of the first assignment
read method

Class FuncMemory contains only one method to read the data of the stored section:

uint64 read( uint64 addr, unsigned short num_of_bytes = 4) const;

It returns a value stored in the byte sequence, which length is equal to num_of_bytes and the address of the first byte of which is equal to addr.

For example, if the functional memory has the following content (the order of bits into the bytes are from left to right):

0x400090    0x400091    0x400092     <-- addresses of bytes
    |           |           |
... | 0001 0011 | 0011 1000 | 1111 0010 | 1101 0000 | ...

Then read( 0x400091, 2) return the value of subsequence | 0011 1000 | 1111 0010 |, which is equal to 0x4f1c.

startPC method

This method just return the start address of .text and has the following declaration:

uint64 startPC() const;

It is used to know where the first instruction of the program is located.

write method

It is similar to the read despite the fact that the data is not read, but written by the specified address. The method has the following interface:

void write( uint64 value, uint64 addr, unsigned short num_of_bytes = 4);
dump method

This method is used to display the contents of the functional memory. It should print the information in the format like dump method of the ElfParser class does.


Unit Testing

The Google C++ Testing Framework is used in our project to check the quality of our code. The classes ElfParser and FuncMemory have already been covered by unit tests, which are located in trunk/func_sim/elf_parser/unit_test.cpp and trunk/func_sim/func_memory/unit_test.cpp correspondently. To run the tests just type make test. It builds the tests and run them. The test log will be printed and you will see passed and failed (if they are) tests.

For example, for ElfParser the output will be something like:

alexandr.titov@helena:~/mipt-mips/func_sim/elf_parser$ make test

Running ./unit_test

[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from Elf_parser_init
[ RUN      ] Elf_parser_init.Process_Wrong_Args_Of_Constr
[       OK ] Elf_parser_init.Process_Wrong_Args_Of_Constr (4 ms)
[----------] 1 test from Elf_parser_init (4 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (5 ms total)
[  PASSED  ] 1 test.
Unit testing for the moduler ELF parser passed SUCCESSFULLY!

Phrase Unit testing for the moduler ELF parser passed SUCCESSFULLY! in the end means that everything was ok and your changes have passed all tests.

Note that in the beginning the unit tests for FuncMemory class will not pass as its interfaces are not implemented. You will see something like this:

alexandr.titov@helena:~/mipt-mips/func_sim/func_memory$ make test

Running ./unit_test

[==========] Running 5 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 1 test from Func_memory_init
[ RUN      ] Func_memory_init.Process_Wrong_Args_Of_Constr
unit_test.cpp:28: Failure
Death test: FuncMemory func_mem( wrong_file_name)
    Result: failed to die.
 Error msg:
[  DEATH   ]
[  FAILED  ] Func_memory_init.Process_Wrong_Args_Of_Constr (6 ms)
[----------] 1 test from Func_memory_init (6 ms total)

[----------] 4 tests from Func_memory
[ RUN      ] Func_memory.StartPC_Method_Test
unit_test.cpp:35: Failure
Value of: 0x4000b0
  Actual: 4194480
Expected: func_mem.startPC()
Which is: 140736709710576
[  FAILED  ] Func_memory.StartPC_Method_Test (1 ms)
[ RUN      ] Func_memory.Read_Method_Test
unit_test.cpp:47: Failure
Value of: right_ret
  Actual: 50462976
Expected: func_mem.read( dataSectAddr)
Which is: 0
[  FAILED  ] Func_memory.Read_Method_Test (0 ms)
[ RUN      ] Func_memory.Write_Read_Initialized_Mem_Test
unit_test.cpp:81: Failure
Value of: right_ret
  Actual: 50462977
Expected: func_mem.read( data_sect_addr)
Which is: 0
[  FAILED  ] Func_memory.Write_Read_Initialized_Mem_Test (1 ms)
[ RUN      ] Func_memory.Write_Read_Not_Initialized_Mem_Test
unit_test.cpp:107: Failure
Value of: right_ret
  Actual: 256
Expected: func_mem.read( write_addr, sizeof( uint16))
Which is: 0
[  FAILED  ] Func_memory.Write_Read_Not_Initialized_Mem_Test (1 ms)
[----------] 4 tests from Func_memory (3 ms total)

[----------] Global test environment tear-down
[==========] 5 tests from 2 test cases ran. (10 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 5 tests, listed below:
[  FAILED  ] Func_memory_init.Process_Wrong_Args_Of_Constr
[  FAILED  ] Func_memory.StartPC_Method_Test
[  FAILED  ] Func_memory.Read_Method_Test
[  FAILED  ] Func_memory.Write_Read_Initialized_Mem_Test
[  FAILED  ] Func_memory.Write_Read_Not_Initialized_Mem_Test

 5 FAILED TESTS
make: *** [test] Error 1

However, when you complete them the testing has to pass successfully and you will see something like:

alexandr.titov@helena:~/mipt-mips/func_sim/func_memory$ make test

Running ./unit_test

[==========] Running 5 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 1 test from Func_memory_init
[ RUN      ] Func_memory_init.Process_Wrong_Args_Of_Constr
[       OK ] Func_memory_init.Process_Wrong_Args_Of_Constr (9 ms)
[----------] 1 test from Func_memory_init (9 ms total)

[----------] 4 tests from Func_memory
[ RUN      ] Func_memory.StartPC_Method_Test
[       OK ] Func_memory.StartPC_Method_Test (1 ms)
[ RUN      ] Func_memory.Read_Method_Test
[       OK ] Func_memory.Read_Method_Test (927 ms)
[ RUN      ] Func_memory.Write_Read_Initialized_Mem_Test
[       OK ] Func_memory.Write_Read_Initialized_Mem_Test (225 ms)
[ RUN      ] Func_memory.Write_Read_Not_Initialized_Mem_Test
[       OK ] Func_memory.Write_Read_Not_Initialized_Mem_Test (0 ms)
[----------] 4 tests from Func_memory (1154 ms total)

[----------] Global test environment tear-down
[==========] 5 tests from 2 test cases ran. (1163 ms total)
[  PASSED  ] 5 tests.
Unit testing for the moduler functional memory passed SUCCESSFULLY!

Otherwise, you did something wrong or your code has an error.

Clone this wiki locally