This commit is contained in:
p-w-rs
2022-10-13 15:01:35 -05:00
parent 2ba3be9aa7
commit 55baac3f0a
26 changed files with 26 additions and 1137 deletions

View File

@@ -99,6 +99,32 @@ pthread_mutex_lock(&parkMutex);
myPark.numOutsidePark++; myPark.numOutsidePark++;
pthread_mutex_unlock(&parkMutex); pthread_mutex_unlock(&parkMutex);
``` ```
or
```c
/*Visitor Task Example*/
sem_t mySemaphore;
//...
// pass semaphore to car (1 at a time)
pthread_mutex_lock(&mailboxMutex); // wait for mailbox
sem_wait(&needPassenger); // wait for passenger request
gMailbox = mySemaphore; // put semaphore in mailbox
sem_post(vmailboxReady); // raise the mailbox flag
sem_wait(&mailAcquired); // wait for delivery
pthread_mutex_unlock(&mailboxMutex); // release mailbox
/*Car Task Example*/
sem_t passengerSems[3];
//...
// get passenger semaphore
sem_post(needPassenger);
sem_wait(mailboxReady); // wait for mail
passengerSems[i] = gMailbox; // get mail
sem_post(mailAcquired); // put flag down
```
# Interfacing with "jurassicTask" # Interfacing with "jurassicTask"

View File

@@ -1,179 +0,0 @@
## Introduction
The purpose of this lab is to design, code, and test a program that uses multi-threading with synchronization mechanisms.
The root vegetable farm grows turnips and radishes for customers. Customers arrive at the farm and wait in line to be served by the farmer. Once they reach the farmer, they place their order for how many turnips and radishes they want to order and go home with their purchase. After a while they need to get more turnips and radishes, so they go back to the farm to purchase more. Customers always order the same amount of vegetables each time they reach the farmer.
The root vegetable farm has a set number of fields. Each turnip takes a set amount of time to grow. Once the turnip is done maturing, the farmer's field hand puts it is put in a storage bin for the farmer to grab and sell to customers. The same is true for radishes.
Fields can only grow a set amount of vegetables each season. Your job is to write a program to simulate the root vegetable farm for a single season (customers purchasing vegetables and fields growing vegetables). The simulation ends when all the fields have finished producing for the season. At that time, any customers waiting to purchase vegetables are immediately sent home and the simulation ends.
## References
You will be using threads for this lab. Each customer, turnip field, and radish field must be a separate thread. You will also have to keep track of the turnips and radishes in the storage bins. There is one bin for each type of vegetable, however the farmer has extra-extra-large bins that never run out of space. You will have to control access to the bins to make sure that customers don't buy vegetables when there aren't enough available.
The following man pages might be helpful for review:
- man pthreads
- man sem_overview
- man usleep
- man sem_wait
- man sem_post
- man pthread_mutex_lock
- man pthread_mutex_unlock
- man pthread_cond_wait
- man pthread_cond_signal
NOTE: In order to view the man pages for the pthread library, they must be installed on your OS image. You can do that by running the command:
```text
sudo apt-get install manpages-posix manpages-posix-dev
```
## The Exercise
Your program must simulate the farm for a single season. Simulation parameters will be given to you in a text file on the command line like previous labs. The format for the text file will be as follows:
- The first line will contain three integers
- The first is the number of turnip fields
- The second is the amount of time it takes for a turnip to grow (in microseconds)
- The third is the number of turnips each turnip field produces for the season
- The second line will contain three integers
- The first is the number of radish fields
- The second is the amount of time it takes for a radish to grow (in microseconds)
- The third is the number of radishes each radish field produces for the season.
- The third line contains a single integer - the number of customers
- The rest of the file contains lines of 3 integers (one for each customer)
- The first is the number of turnips the customer buys when they reach the farmer
- The second is the number of radishes the customer buys when they reach the farmer
- The third is the amount of time the customer waits at home after ordering before they get back in line to buy more turnips and radishes (in microseconds)
All numbers in the line will be separated by spaces. Here is an example:
```text
4 1000 100
3 2000 125
3
1 2 500
3 4 1000
1 0 200
```
The parameters in this file says to simulate the root vegetable farm with 4 turnip fields where turnips take 1000 microseconds to grow and each field grows 100 turnips a season; 3 radish fields where radishes take 2000 microseconds to grow and each field grows 50 radishes a season. There are 3 customers: the first purchases 1 turnip and 2 radishes at a time and waits 500 microseconds between orders; the second purchases 3 turnips and 4 radishes at a time and waits for 1000 microseconds between orders; the third orders 1 turnip and radishes each time and waits 200 microseconds between orders. This simulation has a total of 10 threads (4 for the turnip fields, 3 for the radish fields, and 3 for customers).
NOTE: a single field can produce only a single vegetable at a time. In the example above, one turnip field produces a single turnip every 1000 microseconds.
The simulation will always have at least one turnip field, one radish field, and one customer. Furthermore, the customer will buy at least 1 turnip or 1 radish. They may by 0 of one, however (customer 3 above).
When turnips and radishes are done growing, the farm hand immediately places them into the storage bin (in zero time). Similarly, when customer orders their vegetables, they immediately receive from the storage bin (in zero time). Customers can travel between their home and the farm in zero time as well (they are quite speedy).
All customers initially arrive at the same time and form a line (in any order). After their order is filled, they wait a set amount of time, then go to the end of the line and wait for all customers ahead of them to be served before they can order again.
Your simulation must ensure that a customer waits when they reach the front of the line if there are not enough vegetables in the storage bins. This holds up the line, and nothing can be purchased until enough turnips and radishes are grown. Do not let customers skip others in line even if a customer later in line can have their order satisfied.
You will have to keep a record of how many times a customer had their order filled. This will need to be printed out at the end of your simulation.
You will also have to keep track of how full each storage bin got. In other words, keep track of the largest number of turnips and radishes that were ever in the storage bin. Print this out at the end of your simulation.
Customers can't purchase vegetables that haven't been grown. Make sure your output makes sense. For example, if your simulation only has a single turnip field that grows 100 turnips a season and a single customer that buys 2 turnips at a time, their order can't be filled more than 50 times!
Here is a sample output for the above example parameter file:
```text
The Root Vegetable Farm
Turnip Fields - Number:4 Time:1000 Total:100
Radish Fields - Number:3 Time:2000 Total:125
Customer 0 - Turnips:1 Radishes:2 Wait:500
Customer 1 - Turnips:3 Radishes:4 Wait:1000
Customer 2 - Turnips:1 Radishes:0 Wait:200
Simulation finished:
Max turnips in the turnip bin: 217
Max radishes int the radish bin: 3
Customer 0 got their order filled 65 times
Customer 1 got their order filled 61 times
Customer 2 got their order filled 110 times
```
NOTE: due to the (more or less) random behavior of threads, the output from your root vegetable farm simulation may not exactly match this one. Make sure, however, that your output makes sense.
## Development Tips
- The parameter files can be read in easily with ```fscanf```, just like the previous labs
- As the number of threads is dynamic (specified in the parameter file), you will need to malloc a space for storing statistics. Don't forget to free your mallocs!
- You will need to use concurrency mechanisms for synchronization.
- Semaphores store their count; you can use this to count items in a storage bin.
- Mutex locks store their owner and allow only one thread to obtain the lock.
- Condition variables combine a mutex lock with a condition that is either true or false.
- Do NOT use empty 'while' loops to wait for vegetables.
- All threads for the simulation should start more or less at the same time. Think about how you can do this. Check out the concurrency examples from class for tips.
- Don't forget to synchronize access to the storage bins. All grown vegetables should end up in their bin and shouldn't be lost due to race conditions. Furthermore, only a single customer can buy from the farm at a time. Think mutual exclusion.
- You will need to figure out how to stop your simulation when the day is over. This may take some thought to figure out how to do it correctly to avoid memory leaks.
- The parameter passed to a thread function is a void pointer. Make sure that if you are passing the address of variables on the stack that the value is still good when the thread needs it. Pointers to local variables declared on the stack will become invalid when the stack frame is freed when the function returns.
- Don't forget about using gdb to help with debugging (compile with -g to get additional debug symbols)
NOTE: when using gdb it is helpful to compile your program with additional debug symbols included. These allow gdb to show you more information when running commands like backtrace (bt). To compile with additional debug symbols use the -g flag on gcc. For example:
```text
gcc -Wall -g -o myprog mysourcefile1.c mysourcefile2.c mysourcefile3.c
```
- Using valgrind will be helpful in this lab to ensure you do not have any memory leaks.
- When compiling your program you will need to include an additional compiler flag: -pthread. This tells the compiler to use the pthreads library. The examples use this in the Makefile, so you can see how to use it there as well. For example:
```text
gcc -Wall -g -o myprog -pthread mysourcefile1.c mysourcefile2.c mysourcefile3.c
```
NOTE: the -pthread flag can be anywhere on the command line. It does NOT need to be the last thing on the command line.
## Testing and Debugging
Use the example above to get you started with testing your lab. Creating a file with simpler parameters (fewer customers, turnip fields, and radish fields) might help you debug. You are also required to create some (more than one) of your own parameter files to show that you adequately tested your program. Include those in your submission. Don't forget to test large simulations.
## Deliverables
You will need to include all your source files, test case parameter files, and any other resources you used to complete lab. Please don't just google search for a solution, but if you do use Google for any help, include a description and URL of what you used to help you.
A makefile is useful, but optional for this assignment. If you created a makefile, include it in your submission.
All files should be well documented with function comment blocks and inline comments that explain complicated code routines. While no explicit style guidelines are set for programming in this course, code should be documented and readable. Include your name, section, and lab name in a comment block at the top of each file.
NOTE: do ***NOT*** submit any IDE configuration files (.idea, project files, etc.). Only submit your source files and report.
Prepare a lab report and submit it along with your source code. The report should include the following:
- Your name, section, date, and lab title
- Introduction a description of the lab in your own words
- Design a description of your design decisions in creating your solution
- Resources a description of any external resources you used to complete the lab
- Build instructions on how to build and run your program. Include the exact commands that are necessary
- Analysis Program needs to print out, for each customer, how many times they had their order filled. Use this information to evaluate the fairness of your implementation.
- Is your implementation fair given the parameters for vegetables a customer orders and how long they wait between ordering?
- What is "fair" in your own words?
- In this implementation, when a customer arrives at the front of the line, if there are not enough vegetables, they hold up the entire line and wait until their vegetables have grown. What if instead, when a customer reached the front of the line if not enough vegetables were ready, they skip their ordering opportunity and immediately return to the end of the line without ordering? Given your definition of fair, is this behavior fair? Why or why not?
- Deli counters, butcher stores, restaurants, etc. use a number system where a customer orders immediately, receives a number, and waits while their order is prepared. How would you change your implementation (in terms of how concurrency mechanisms are used) to handle this scenario?
- The farm storage bins have limitless capacity. Does this make sense in practice? Why or why not? How would your code need to be modified to handle a bin with a capacity limit?
- Conclusion
- Summary of what you learned in the lab
- What specifically was challenging about this lab?
- What did you like about it?
- What could we do to improve it for others?
NOTE: You should ensure that this program compiles without warning (-Wall and -Wextra) prior to submitting.
Prepare a zip file with all submitted files and upload the file to Canvas per your instructor's instructions.
## Grading Criteria
- (35 Points) Report
- (5 Points) Report Introduction - Thorough description of the lab in your own words.
- (5 Points) Design and Testing Methodology - Detailed description of design and method for testing your implementation.
- (20 Points) Analysis - Answers to the analysis questions
- (5 Points) Conclusion - Thorough conclusion with description of what you learned, what you liked, and suggestions for lab improvements.
- (5 Points) Documented Resources - Description of external resources used to complete the lab
- (5 Points) Correct Submission - Followed submission instructions (e.g. IDE project files are not submitted)
- (5 Points) Build - Code compiles without warnings or errors
- (10 Points) Test Cases - Thoroughness of submitted test cases
- (35 Points) Instructor Tests - Implementation passes all instructor test cases
- (5 Points) Memory Management - Program execution is free from memory leaks

View File

@@ -1,6 +0,0 @@
4 1000 100
3 2000 125
3
1 2 500
3 4 1000
1 0 200

View File

@@ -1,4 +0,0 @@
1 1000 1
1 1000 1
1
1 1 500

View File

@@ -1,9 +0,0 @@
10 100 100
10 100 100
6
1 1 10
1 1 10
1 1 10
1 1 10
1 1 10
1 1 10

View File

@@ -1,28 +0,0 @@
4 1000 100
3 2000 125
25
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100
1 1 100

View File

@@ -1,6 +0,0 @@
200 1000 10
100 1000 10
3
1 2 500
3 4 1000
1 0 200

View File

@@ -1,7 +0,0 @@
1 1000 10
1 1000 10
4
1 0 200
1 0 200
1 0 200
1 0 200

View File

@@ -1,4 +0,0 @@
2 500 1
2 500 1
1
100 100 200

View File

@@ -1,232 +0,0 @@
#ifndef _ELF_H
#define _ELF_H
#include <stdint.h>
#include "errorCode.h"
/* https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html */
/* ELF Data Types */
typedef uint32_t Elf32_Addr;
typedef uint32_t Elf32_Off;
typedef uint16_t Elf32_Half;
typedef uint32_t Elf32_Word;
typedef int32_t Elf32_Sword;
typedef uint64_t Elf64_Addr;
typedef uint64_t Elf64_Off;
typedef uint16_t Elf64_Half;
typedef uint32_t Elf64_Word;
typedef int32_t Elf64_Sword;
typedef uint64_t Elf64_Xword;
typedef int64_t Elf64_Sxword;
/* ELF Magic */
#define ELFMAG0 0x7f
#define ELFMAG1 'E'
#define ELFMAG2 'L'
#define ELFMAG3 'F'
/* Class */
#define ELFCLASSNONE 0
#define ELFCLASS32 1
#define ELFCLASS64 2
/* Data encoding */
#define ELFDATANONE 0
#define ELFDATA2LSB 1
#define ELFDATA2MSB 2
/* Version */
#define EV_NONE 0
#define EV_CURRENT 1
/* Type */
#define ET_NONE 0
#define ET_REL 1
#define ET_EXEC 2
#define ET_DYN 3
#define ET_CORE 4
#define ET_LOOS 0xfe00
#define ET_HIOS 0xfeff
#define ET_LOPROC 0xff00
#define ET_HIPROC 0xffff
#define EI_NIDENT 16
typedef struct {
uint8_t e_ident[EI_NIDENT];
Elf64_Half e_type;
Elf64_Half e_machine;
Elf64_Word e_version;
Elf64_Addr e_entry;
Elf64_Off e_phoff;
Elf64_Off e_shoff;
Elf64_Word e_flags;
Elf64_Half e_ehsize;
Elf64_Half e_phentsize;
Elf64_Half e_phnum;
Elf64_Half e_shentsize;
Elf64_Half e_shnum;
Elf64_Half e_shstrndx;
} Elf64_Ehdr;
/* Program header types */
#define PT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define PT_INTERP 3
#define PT_NOTE 4
#define PT_SHLIB 5
#define PT_PHDR 6
#define PT_TLS 7
#define PT_LOOS 0x60000000
#define PT_HIOS 0x6fffffff
#define PT_LOPROC 0x70000000
#define PT_HIPROC 0x7fffffff
typedef struct {
Elf64_Word p_type;
Elf64_Word p_flags;
Elf64_Off p_offset;
Elf64_Addr p_vaddr;
Elf64_Addr p_paddr;
Elf64_Xword p_filesz;
Elf64_Xword p_memsz;
Elf64_Xword p_align;
} Elf64_Phdr;
/* Section header type */
#define SHT_NULL 0
#define SHT_PROGBITS 1
#define SHT_SYMTAB 2
#define SHT_STRTAB 3
#define SHT_RELA 4
#define SHT_HASH 5
#define SHT_DYNAMIC 6
#define SHT_NOTE 7
#define SHT_NOBITS 8
#define SHT_REL 9
#define SHT_SHLIB 10
#define SHT_DYNSYM 11
#define SHT_INIT_ARRAY 14
#define SHT_FINI_ARRAY 15
#define SHT_PREINIT_ARRAY 16
#define SHT_GROUP 17
#define SHT_SYMTAB_SHNDX 18
#define SHT_LOOS 0x60000000
#define SHT_HIOS 0x6fffffff
#define SHT_LOPROC 0x70000000
#define SHT_HIPROC 0x7fffffff
#define SHT_LOUSER 0x80000000
#define SHT_HIUSER 0xffffffff
/* Section header name index */
#define SHN_UNDEF 0
#define SHN_LORESERVE 0xff00
#define SHN_LOPROC 0xff00
#define SHN_HIPROC 0xff1f
#define SHN_ABS 0xfff1
#define SHN_COMMON 0xfff2
#define SHN_HIRESERVE 0xffff
/* Section header flags */
#define SHF_WRITE 0x1
#define SHF_ALLOC 0x2
#define SHF_EXECINSTR 0x4
#define SHF_MERGE 0x10
#define SHF_STRINGS 0x20
#define SHF_INFO_LINK 0x40
#define SHF_LINK_ORDER 0x80
#define SHF_OS_NONCONFORMING 0x100
#define SHF_GROUP 0x200
#define SHF_TLS 0x400
#define SHF_MASKOS 0x0ff00000
#define SHF_MASKPROC 0xf0000000
typedef struct {
Elf64_Word sh_name;
Elf64_Word sh_type;
Elf64_Xword sh_flags;
Elf64_Addr sh_addr;
Elf64_Off sh_offset;
Elf64_Xword sh_size;
Elf64_Word sh_link;
Elf64_Word sh_info;
Elf64_Xword sh_addralign;
Elf64_Xword sh_entsize;
} Elf64_Shdr;
/* Symbol table index values */
#define STN_UNDEF 0
typedef struct {
Elf64_Word st_name;
uint8_t st_info;
uint8_t st_other;
Elf64_Half st_shndx;
Elf64_Addr st_value;
Elf64_Xword st_size;
} Elf64_Sym;
typedef struct {
Elf64_Addr r_offset;
Elf64_Xword r_info;
} Elf64_Rel;
typedef struct {
Elf64_Addr r_offset;
Elf64_Xword r_info;
Elf64_Sxword r_addend;
} Elf64_Rela;
#define R_X86_64_NONE 0
#define R_X86_64_64 1
#define R_X86_64_PC32 2
#define R_X86_64_GOT32 3
#define R_X86_64_PLT32 4
#define R_X86_64_COPY 5
#define R_X86_64_GLOB_DAT 6
#define R_X86_64_JUMP_SLOT 7
#define R_X86_64_RELATIVE 8
#define R_X86_64_GOTPCREL 9
#define R_X86_64_32 10
#define R_X86_64_32S 11
#define R_X86_64_16 12
#define R_X86_64_PC16 13
#define R_X86_64_8 14
#define R_X86_64_PC8 15
#define R_X86_64_DPTMOD64 16
#define R_X86_64_DTPOFF64 17
#define R_X86_64_TPOFF64 18
#define R_X86_64_TLSGD 19
#define R_X86_64_TLSLD 20
#define R_X86_64_DTPOFF32 21
#define R_X86_64_GOTTPOFF 22
#define R_X86_64_TPOFF32 23
#define R_X86_64_PC64 24
#define R_X86_64_GOTOFF64 25
#define R_X86_64_GOTPC32 26
#define R_X86_64_SIZE32 32
#define R_X86_64_SIZE64 33
#define ELF64_R_SYM(i) ((i)>>32)
#define ELF64_R_TYPE(i) ((i)&0xffffffffL)
#define ELF64_R_INFO(s,t) (((s)<<32)+((t)&0xffffffffL))
typedef struct elf_file {
int size;
void* contents;
Elf64_Ehdr* ehdr;
Elf64_Phdr* phdr;
Elf64_Shdr* shdr;
} elf_file;
errorCode_t load_elf(const char* filename, elf_file* file);
errorCode_t get_elf_proc_size(elf_file* file, uint64_t* size);
errorCode_t populate_elf_proc(elf_file* file, void* tgt_addr);
errorCode_t find_symbol(elf_file* file, const char* name, uint64_t* offset);
errorCode_t free_elf(elf_file* file);
#endif

View File

@@ -1,15 +0,0 @@
#ifndef _ERRORCODE_H
#define _ERRORCODE_H
typedef int errorCode_t;
#define IS_ERROR(n) (n < SUCCESS)
#define SUCCESS 0
#define EBADPARM -1
#define ENOTFOUND -2
#define EBADEXEC -3
#define EMEMORY -4
#define EFILESYS -5
#endif

View File

@@ -1,22 +0,0 @@
#ifndef _FS_H
#define _FS_H
#include "errorCode.h"
/* fs_open flags */
#define O_RDONLY 00000000
#define O_WRONLY 00000001
#define O_RDWR 00000002
/* fs_seek whence */
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
errorCode_t fs_open(const char* path, int flags, int* fd);
errorCode_t fs_write(int fd, const void *buf, int count, int* bytes_written);
errorCode_t fs_read(int fd, void *buf, int count, int* bytes_read);
errorCode_t fs_seek(int fd, int offset, int whence, int* distance);
errorCode_t fs_close(int fd);
#endif

View File

@@ -1,15 +0,0 @@
#ifndef _MEMORY_H
#define _MEMORY_H
#include "errorCode.h"
#define PAGE_SIZE 4096
#define PAGE_ALIGN(n) (n + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1)
errorCode_t allocate_user_memory(int size, void** addr);
errorCode_t free_user_memory(void* addr, int size);
errorCode_t get_kernel_memory(int size, void** addr);
errorCode_t free_kernel_memory(void* addr, int size);
#endif

View File

@@ -1,19 +0,0 @@
#ifndef _PROCESS_H
#define _PROCESS_H
#define STACK_SIZE 8192
#include "elf.h"
typedef struct process {
void* entry_point;
void* exec_start;
void* stack_start;
void* stack_end;
int size;
} process;
errorCode_t create_process(const char* file, process* proc);
errorCode_t free_process(process* proc);
#endif

View File

@@ -1,6 +0,0 @@
#ifndef _SYSCALL_H
#define _SYSCALL_H
long system_call(long n, ...);
#endif

View File

@@ -1,5 +0,0 @@
ENTRY(main)
SECTIONS
{
. = 0x000000; /* Image starts here */
}

View File

@@ -1,16 +0,0 @@
CC=gcc
CFLAGS=-g -Wall -Iinclude
LFLAGS=
OBJS=$(patsubst %.c,%.o,$(wildcard src/*.c)) $(patsubst %.c,%.o,$(wildcard src/fs/*.c))
all: ttos
ttos: $(OBJS)
$(CC) $(LFLAGS) -o ttos $(OBJS)
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f $(OBJS) ttos

View File

@@ -1,307 +0,0 @@
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "fs/fs.h"
#include "syscall.h"
#include "elf.h"
#include "process.h"
#include "memory.h"
static int validate_magic(uint8_t e_ident[EI_NIDENT]) {
// ELF Magic must match
if(e_ident[0] != ELFMAG0 || e_ident[1] != ELFMAG1 || e_ident[2] != ELFMAG2 || e_ident[3] != ELFMAG3) {
return -1;
}
// Currently, only support 64 bit executables
if(e_ident[4] != ELFCLASS64) {
return -1;
}
// Currently, only support LSB byte order executables
if(e_ident[5] != ELFDATA2LSB) {
return -1;
}
// Currently, only support ELF current version
if(e_ident[6] != EV_CURRENT) {
return -1;
}
return 0;
}
static errorCode_t get_file_data(const char* path, elf_file* file) {
int rc;
// Open the file
int fd = 0;
rc = fs_open(path, O_RDONLY, &fd);
if(IS_ERROR(rc)) {
return rc;
}
// Seek to the beginning and then the end
// to get the file size
fs_seek(fd, 0, SEEK_SET, 0);
rc = fs_seek(fd, 0, SEEK_END, &file->size);
if(IS_ERROR(file->size) || file->size == 0) {
return EBADEXEC;
}
rc = get_kernel_memory(file->size, &file->contents);
if(IS_ERROR(rc)) {
return rc;
}
// Seek to the beginning and read the file data
fs_seek(fd, 0, SEEK_SET, 0);
int bytes_read = 0;
rc = fs_read(fd, file->contents, file->size, &bytes_read);
// Check error if error use error code if one was returned
if(IS_ERROR(rc)) {
free_kernel_memory(file->contents, file->size);
fs_close(fd);
return rc;
}
// Check error ELIBBAD if data read != size
if(bytes_read != file->size) {
free_kernel_memory(file->contents, file->size);
fs_close(fd);
return EBADEXEC;
}
// Close the file and return
fs_close(fd);
return 0;
}
static Elf64_Phdr* get_program_header(elf_file* file, int index) {
return (Elf64_Phdr*)((char*)file->phdr + index * file->ehdr->e_phentsize);
}
static Elf64_Shdr* get_section_header(elf_file* file, int index) {
return (Elf64_Shdr*)((char*)file->shdr + index * file->ehdr->e_shentsize);
}
static void* find_external_symbol(elf_file* file, Elf64_Shdr *symtbl_shdr, Elf64_Word st_name) {
if(symtbl_shdr->sh_link == 0) {
return 0;
}
Elf64_Shdr* strtbl_hdr = get_section_header(file, symtbl_shdr->sh_link);
char* strtbl = (char*)((uint8_t*)file->contents + strtbl_hdr->sh_offset);
char* symbol_name = &(strtbl[st_name]);
if(strncmp(symbol_name, "system_call", 7) == 0) {
return system_call;
}
return 0;
}
static errorCode_t relocate_symbols(elf_file* file, void* tgt_addr) {
for(int i = 0; i < file->ehdr->e_shnum; i++) {
Elf64_Shdr* shdr = get_section_header(file, i);
if(shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) {
// Retrieve the sections headers for the relocation section, linked symbol table, and target section
Elf64_Shdr* rel_shdr = shdr;
Elf64_Shdr *symtbl_shdr = get_section_header(file, rel_shdr->sh_link);
Elf64_Shdr *tgt_shdr = get_section_header(file, rel_shdr->sh_info);
// Retrieve the linked symbol table
Elf64_Sym* symtbl = (Elf64_Sym*)((uint8_t*)file->contents + symtbl_shdr->sh_offset);
// Loop through each relocation entry and update the target
int entry_count = rel_shdr->sh_size / rel_shdr->sh_entsize;
for (int j = 0; j < entry_count; j++) {
Elf64_Rela* relhdr = (Elf64_Rela*)((uint8_t*)file->contents + rel_shdr->sh_offset + j*rel_shdr->sh_entsize);
Elf64_Xword symbol = ELF64_R_SYM(relhdr->r_info);
Elf64_Xword type = ELF64_R_TYPE(relhdr->r_info);
// Symbol address
// For relocatable files - start of the exec + offset to the section containing the symbol + offset to the symbol in the section (aka symbol value)
// For all other files - start of the exec + symbol value (aka virtual address)
// For undefined symbols - search for the symbol externally
void* symbol_address = 0;
if(file->ehdr->e_type == ET_REL) {
symbol_address = (void*)((uint8_t*)tgt_addr + file->shdr[symtbl[symbol].st_shndx].sh_offset + symtbl[symbol].st_value);
} else if(symtbl[symbol].st_shndx == STN_UNDEF) {
symbol_address = find_external_symbol(file, symtbl_shdr, symtbl[symbol].st_name);
} else {
symbol_address = (void*)((uint8_t*)tgt_addr + symtbl[symbol].st_value);
}
if(symbol_address == 0) {
return -1;
}
// Location in section to patch
// For relocatable files - start of exec + offset to patch section + offset in the section
// For all other files - start of the exec + relocation offset value (aka virtual address)
void* patch_location = file->ehdr->e_type == ET_REL ?
(void*)((uint8_t*)tgt_addr + tgt_shdr->sh_offset + relhdr->r_offset) :
(void*)((uint8_t*)tgt_addr + relhdr->r_offset);
// retrieve the addend, only applies to RELA entries
Elf64_Sxword addend = file->shdr[i].sh_type == SHT_RELA ? relhdr->r_addend : 0;
// https://refspecs.linuxbase.org/elf/x86_64-abi-0.98.pdf page 69
switch(type) {
case R_X86_64_PC8:
*(uint8_t*)patch_location = (uint8_t*)symbol_address + addend - (uint8_t*)patch_location;
break;
case R_X86_64_PC16:
*(uint16_t*)patch_location = (uint8_t*)symbol_address + addend - (uint8_t*)patch_location;
case R_X86_64_PC32:
case R_X86_64_PLT32:
*(uint32_t*)patch_location = (uint8_t*)symbol_address + addend - (uint8_t*)patch_location;
break;
case R_X86_64_PC64:
*(uint64_t*)patch_location = (uint8_t*)symbol_address + addend - (uint8_t*)patch_location;
break;
case R_X86_64_GLOB_DAT:
case R_X86_64_JUMP_SLOT:
*(uint64_t*)patch_location = (uint64_t)symbol_address;
break;
}
// printf("%lX %lX %016lX %016lX\n", symbol, type, (uint64_t)symbol_address, (uint64_t)patch_location);
}
}
}
return SUCCESS;
}
errorCode_t load_elf(const char* path, elf_file* file) {
if(file == 0 || path == 0) {
return EBADPARM;
}
// Initialize the file and retrieve the file data
memset(file, 0x00, sizeof(elf_file));
int rc = get_file_data(path, file);
if(IS_ERROR(rc)) {
return rc;
}
// Validate the file magic value
if(validate_magic(file->contents) != 0) {
free_elf(file);
return EBADEXEC;
}
file->ehdr = (Elf64_Ehdr*)file->contents;
/* Only support dynamic linked files */
if(file->ehdr->e_type != ET_DYN) {
free_elf(file);
return EBADEXEC;
}
if(file->ehdr->e_phoff == 0) {
free_elf(file);
return EBADEXEC;
}
file->phdr = (Elf64_Phdr*)((uint8_t*)file->contents + file->ehdr->e_phoff);
if(file->ehdr->e_shoff == 0) {;
free_elf(file);
return EBADEXEC;
}
file->shdr = (Elf64_Shdr*)((uint8_t*)file->contents + file->ehdr->e_shoff);
return SUCCESS;
}
errorCode_t get_elf_proc_size(elf_file* file, uint64_t* size) {
if(file == 0 || size == 0) {
return EBADPARM;
}
// Determine the size to allocate for the process
// Determined by the highest address of any memory segment in the program header
Elf64_Phdr* highest_header = 0;
for(int i = 0; i < file->ehdr->e_phnum; i++) {
Elf64_Phdr *phdr = get_program_header(file, i);
if (phdr->p_type == PT_LOAD) {
if (highest_header == 0 || highest_header->p_vaddr < phdr->p_vaddr) {
highest_header = phdr;
}
}
}
*size = (uint64_t)highest_header->p_vaddr + (uint64_t)highest_header->p_memsz;
return SUCCESS;
}
errorCode_t populate_elf_proc(elf_file* file, void* tgt_addr) {
if(file == 0 || tgt_addr == 0) {
return EBADPARM;
}
// Copy program sections into the process
for(int i = 0; i < file->ehdr->e_shnum; i++) {
Elf64_Shdr* shdr = get_section_header(file, i);
if(shdr && (shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr > 0) {
memcpy(((uint8_t*)tgt_addr) + shdr->sh_addr, ((uint8_t*)file->contents) + shdr->sh_offset, shdr->sh_size);
}
}
// Relocate symbols
return relocate_symbols(file, tgt_addr);
}
errorCode_t find_symbol(elf_file* file, const char* name, uint64_t* offset) {
if(offset == 0) {
return EBADPARM;
}
// Search for a symbol
// Iterate through each section
for(int i = 0; i < file->ehdr->e_shnum; i++) {
// Find a section that represents a symbol table
Elf64_Shdr* shdr = get_section_header(file, i);
if(shdr->sh_type == SHT_SYMTAB) {
// Retrieve the corresponding string table
Elf64_Shdr* strtbl_hdr = get_section_header(file, shdr->sh_link);
char* strtbl = (char*)((uint8_t*)file->contents + strtbl_hdr->sh_offset);
// Iterate through each entry searching for the symbol string
int entry_count = shdr->sh_size / shdr->sh_entsize;
for(int j = 0; j < entry_count; j++) {
Elf64_Sym* symtbl = (Elf64_Sym*)((uint8_t*)file->contents + shdr->sh_offset + j*shdr->sh_entsize);
char* symbol_name = &(strtbl[symtbl->st_name]);
// Check symbol name and return offset
// For relocatable files - offset to symbol table + offset to the symbol (aka symbol value)
// For all other files - symbol value (aka virtual address)
if(strcmp(symbol_name, name) == 0) {
if(file->ehdr->e_type == ET_REL) {
*offset = file->shdr[symtbl->st_shndx].sh_offset + symtbl->st_value;
} else {
*offset = symtbl->st_value;
}
return SUCCESS;
}
}
}
}
return ENOTFOUND;
}
errorCode_t free_elf(elf_file* file) {
free_kernel_memory(file->contents, file->size);
memset(file, 0x00, sizeof(elf_file));
return SUCCESS;
}

View File

@@ -1,57 +0,0 @@
#include <unistd.h>
#include <sys/syscall.h>
#include "errorCode.h"
errorCode_t fs_open(const char* path, int flags, int* fd) {
if(fd == 0) {
return EBADPARM;
}
int rc = syscall(SYS_open, path, flags);
if(rc == -1) {
return EFILESYS;
}
*fd = rc;
return SUCCESS;
}
errorCode_t fs_write(int fd, const void *buf, int count, int* bytes_written) {
int rc = syscall(SYS_write, fd, buf, count);
if(rc == -1) {
return EFILESYS;
}
if(bytes_written != 0) {
*bytes_written = rc;
}
return SUCCESS;
}
errorCode_t fs_read(int fd, void *buf, int count, int* bytes_read) {
int rc = syscall(SYS_read, fd, buf, count);
if(rc == -1) {
return EFILESYS;
}
if(bytes_read != 0) {
*bytes_read = rc;
}
return SUCCESS;
}
errorCode_t fs_seek(int fd, int offset, int whence, int* distance) {
int rc = syscall(SYS_lseek, fd, offset, whence);
if(rc == -1) {
return EFILESYS;
}
if(distance != 0) {
*distance = rc;
}
return SUCCESS;
}
errorCode_t fs_close(int fd) {
int rc = syscall(SYS_close, fd);
if(rc == -1) {
return EFILESYS;
}
return SUCCESS;
}

View File

@@ -1,16 +0,0 @@
#include <stdio.h>
#include "process.h"
int main(int argc, char* argv[]) {
process out;
printf("%d\n", create_process("test", &out));
int (*main_exec)() = out.entry_point;
int ret_val = main_exec();
printf("%d\n", ret_val);
free_process(&out);
return 0;
}

View File

@@ -1,52 +0,0 @@
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include "errorCode.h"
#include "memory.h"
int allocate_user_memory(int size, void** addr) {
if(addr == 0) {
return EBADPARM;
}
uint64_t aligned_size = PAGE_ALIGN(size);
*addr = mmap(NULL, aligned_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(*addr == MAP_FAILED) {
*addr = 0;
return EMEMORY;
}
return SUCCESS;
}
int free_user_memory(void* addr, int size) {
if(addr == 0) {
return EBADPARM;
}
int rc = munmap(addr, size);
if(rc == -1) {
return EMEMORY;
}
return SUCCESS;
}
int get_kernel_memory(int size, void** addr) {
if(addr == 0) {
return EBADPARM;
}
*addr = malloc(size);
if(*addr == 0) {
return EMEMORY;
}
return SUCCESS;
}
int free_kernel_memory(void* addr, int size) {
if(addr == 0) {
return EBADPARM;
}
free(addr);
return SUCCESS;
}

View File

@@ -1,80 +0,0 @@
#include <stdint.h>
#include <string.h>
#include "errorCode.h"
#include "elf.h"
#include "process.h"
#include "memory.h"
static errorCode_t allocate_process(int size, process* proc) {
int aligned_size = PAGE_ALIGN(size);
int rc = allocate_user_memory(aligned_size + STACK_SIZE, &(proc->exec_start));
if(IS_ERROR(rc)) {
return rc;
}
proc->stack_start = ((uint8_t*)(proc->exec_start) + aligned_size);
proc->stack_end = ((uint8_t*)(proc->stack_start) + STACK_SIZE);
proc->size = aligned_size + STACK_SIZE;
return SUCCESS;
}
int create_process(const char* path, process* proc) {
if(proc == 0) {
return EBADPARM;
}
// Load the executable file
elf_file file;
int rc = load_elf(path, &file);
if(IS_ERROR(rc)) {
return rc;
}
// Allocate the process address space
uint64_t proc_size = 0;
rc = get_elf_proc_size(&file, &proc_size);
if(IS_ERROR(rc)) {
free_elf(&file);
return rc;
}
memset(proc, 0x00, sizeof(process));
rc = allocate_process(proc_size, proc);
if(IS_ERROR(rc)) {
free_elf(&file);
return rc;
}
// Populate the process address space
rc = populate_elf_proc(&file, proc->exec_start);
if(IS_ERROR(rc)) {
free_elf(&file);
free_process(proc);
return rc;
}
// Set the process entry point
uint64_t offset_to_main = 0;
rc = find_symbol(&file, "main", &offset_to_main);
if(IS_ERROR(rc)) {
free_elf(&file);
free_process(proc);
return rc;
}
proc->entry_point = (void*)((uint8_t*)proc->exec_start + offset_to_main);
// Free the executable - no longer needed since process has been created
free_elf(&file);
return SUCCESS;
}
errorCode_t free_process(process* proc) {
if(proc == 0) {
return EBADPARM;
}
free_user_memory(proc->exec_start, proc->size);
proc->exec_start = 0;
proc->stack_start = 0;
proc->stack_end = 0;
proc->size = 0;
return SUCCESS;
}

View File

@@ -1,21 +0,0 @@
#include <stdarg.h>
#include <stdio.h>
#include "fs/fs.h"
long system_call(long n, ...) {
long rc = 0;
va_list ap;
va_start(ap, n);
if(n == 1) {
int fd = va_arg(ap, int);
const char* buf = va_arg(ap, const char*);
int count = va_arg(ap, int);
int* bytes_written = va_arg(ap, int*);
rc = fs_write(fd, buf, count, bytes_written);
}
return rc;
}

View File

@@ -1,27 +0,0 @@
//#include <stdio.h>
long system_call(long n, ...);
int j = 3;
int i = 10;
int foo3(int b);
int foo2(int b) {
return foo3(b) + 2;
}
int foo1(int a) {
return foo2(a) + 2 + i;
}
int main(int argc, char* argv[]) {
// printf("hi\n");
// j = 10;
system_call(1, 1, "abc\n", 4);
int result = foo1(5 + j);
system_call(1, 1, result, 4);
system_call(1, 1, "\n", 1);
return foo1(5 + j);
// return foo1(5 + j);
}

View File

@@ -1,3 +0,0 @@
int foo3(int b) {
return b + 3;
}

View File

@@ -1 +0,0 @@
pandoc --webtex --ascii -f markdown -t html5 -o .\%1.html .\%1.md