Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .checkpatch.ignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Ignore directories containing third-party files
chapters/compute/synchronization/drills/tasks/threadsafe-data-struct/support
content/assignments/async-web-server/src/http-parser
content/assignments/elf-loader/tests
1 change: 1 addition & 0 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ docusaurus:
subsections:
- Mini Libc/: chapters/software-stack/libc/projects/mini-libc/
- Memory Allocator/: content/assignments/memory-allocator/
- ELF Loader/: content/assignments/elf-loader/
- Parallel Firewall/: content/assignments/parallel-firewall/
- Mini Shell/: content/assignments/minishell/
- Asynchronous Web Server/: content/assignments/async-web-server/
Expand Down
368 changes: 368 additions & 0 deletions content/assignments/elf-loader/README.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions content/assignments/elf-loader/img/auxv-example.drawio.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions content/assignments/elf-loader/img/auxv.drawio.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions content/assignments/elf-loader/img/stack-layout.drawio.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
9 changes: 9 additions & 0 deletions content/assignments/elf-loader/src/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CFLAGS= -g -static-pie -o

all: elf-loader

elf-loader: elf-loader.c
gcc $(CFLAGS) $@ $<

clean:
-rm -f *.o elf-loader
102 changes: 102 additions & 0 deletions content/assignments/elf-loader/src/elf-loader.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: BSD-3-Clause

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>

void *map_elf(const char *filename)
{
// This part helps you store the content of the ELF file inside the buffer.
struct stat st;
void *file;
int fd;

fd = open(filename, O_RDONLY);
if (fd < 0) {
perror("open");
exit(1);
}

fstat(fd, &st);

file = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (file == MAP_FAILED) {
perror("mmap");
close(fd);
exit(1);
}

return file;
}

void load_and_run(const char *filename, int argc, char **argv, char **envp)
{
// Contents of the ELF file are in the buffer: elf_contents[x] is the x-th byte of the ELF file.
void *elf_contents = map_elf(filename);

/**
* TODO: ELF Header Validation
* Validate ELF magic bytes - "Not a valid ELF file" + exit code 3 if invalid.
* Validate ELF class is 64-bit (ELFCLASS64) - "Not a 64-bit ELF" + exit code 4 if invalid.
*/

/**
* TODO: Load PT_LOAD segments
* For minimal syscall-only binaries.
* For each PT_LOAD segment:
* - Map the segments in memory. Permissions can be RWX for now.
*/

/**
* TODO: Load Memory Regions with Correct Permissions
* For each PT_LOAD segment:
* - Set memory permissions according to program header p_flags (PF_R, PF_W, PF_X).
* - Use mprotect() or map with the correct permissions directly using mmap().
*/

/**
* TODO: Support Static Non-PIE Binaries with libc
* Must set up a valid process stack, including:
* - argc, argv, envp
* - auxv vector (with entries like AT_PHDR, AT_PHENT, AT_PHNUM, etc.)
* Note: Beware of the AT_RANDOM, AT_PHDR entries, the application will
* crash if you do not set them up properly.
*/
void *sp = NULL;

/**
* TODO: Support Static PIE Executables
* Map PT_LOAD segments at a random load base.
* Adjust virtual addresses of segments and entry point by load_base.
* Stack setup (argc, argv, envp, auxv) same as above.
*/

// TODO: Set the entry point and the stack pointer
void (*entry)() = NULL;

// Transfer control
__asm__ __volatile__(
"mov %0, %%rsp\n"
"xor %%rbp, %%rbp\n"
"jmp *%1\n"
:
: "r"(sp), "r"(entry)
: "memory"
);
}

int main(int argc, char **argv, char **envp)
{
if (argc < 2) {
fprintf(stderr, "Usage: %s <static-elf-binary>\n", argv[0]);
exit(1);
}

load_and_run(argv[1], argc - 1, &argv[1], envp);
return 0;
}
Empty file.
41 changes: 41 additions & 0 deletions content/assignments/elf-loader/tests/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export SRC_PATH ?= $(realpath ../src)
export UTILS_PATH ?= $(realpath ../utils)

CC=gcc
NASM=nasm
CFLAGS_NO_PIE=-g -static -fcf-protection=none -fno-PIC -o
CFLAGS=-g -static-pie -o
LDFLAGS_NO_PIE=-no-pie
LDFLAGS=
NASMFLAGS=-felf64 -o

SNIPPETS_PATH=$(PWD)/snippets

.PHONY: all src snippets clean_src clean_snippets check lint

all: src snippets

src:
$(MAKE) -C $(SRC_PATH)

snippets:
$(MAKE) -C $(SNIPPETS_PATH)

clean_snippets:
$(MAKE) -C $(SNIPPETS_PATH) clean

clean_src:
$(MAKE) -C $(SRC_PATH) clean

check:
$(MAKE) clean_src clean_snippets src snippets
./run_tests.sh

check-fast:
$(MAKE) clean_src clean_snippets src snippets
./run_tests.sh

lint:
-cd .. && checkpatch.pl -f src/*.c tests/snippets/*.c
-cd .. && checkpatch.pl -f checker/*.sh tests/*.sh
-cd .. && shellcheck checker/*.sh tests/*.sh
136 changes: 136 additions & 0 deletions content/assignments/elf-loader/tests/grade.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#!/bin/bash
# SPDX-License-Identifier: BSD-3-Clause

# Grade style based on build warnings and linter warnings / errors.
# Points are subtracted from the maximum amount of style points (10).
# - For 15 or more build warnings, all points (10) are subtracted.
# - For [10,15) build warnings, 6 points are subtracted.
# - For [5,10) build warnings, 4 points are subtracted.
# - For [1,5) build warnings, 2 points are subtracted.
# - For 25 ore more linter warnings / errors, all points (10) are subtracted.
# - For [15,25) linter warnings / errors, 6 points are subtracted.
# - For [7,15) linter warnings / errors, 4 points are subtracted.
# - For [1,7) linter warnings / errors, 2 points are subtracted.
# Final style points are between 0 and 10. Results cannot be negative.
#
# Result (grade) is stored in style_grade.out file.
# Collect summary in style_summary.out file.

function grade_style()
{
compiler_warn=$(< checker.out grep -v 'unused parameter' | grep -v 'unused variable' | \
grep -v "discards 'const'" | grep -c '[0-9]\+:[0-9]\+: warning:')

compiler_down=0
if test "$compiler_warn" -ge 15; then
compiler_down=10
elif test "$compiler_warn" -ge 10; then
compiler_down=6
elif test "$compiler_warn" -ge 5; then
compiler_down=4
elif test "$compiler_warn" -ge 1; then
compiler_down=2
fi

cpplint=$(< linter.out grep "Total errors found:" | rev | cut -d ' ' -f 1 | rev)
checkpatch_err=$(< linter.out grep 'total: [0-9]* errors' | grep -o '[0-9]* errors,' | \
cut -d ' ' -f 1 | paste -s -d '+' | bc)
checkpatch_warn=$(< linter.out grep 'total: [0-9]* errors' | grep -o '[0-9]* warnings,' | \
cut -d ' ' -f 1 | paste -s -d '+' | bc)
if test -z "$checkpatch_err"; then
checkpatch_err=0
fi
if test -z "$checkpatch_warn"; then
checkpatch_warn=0
fi
checkpatch=$((checkpatch_err + checkpatch_warn))
checker_all=$((cpplint + checkpatch))

checker_down=0
if test "$checker_all" -ge 25; then
checker_down=10
elif test "$checker_all" -ge 15; then
checker_down=6
elif test "$checker_all" -ge 7; then
checker_down=4
elif test "$checker_all" -ge 1; then
checker_down=2
fi

full_down=$((compiler_down + checker_down))

if test "$full_down" -gt 10; then
full_down=10
fi
style_grade=$((10 - full_down))

echo "$style_grade" > style_grade.out

{
< linter.out grep -v 'unused parameter' | grep -v 'unused variable' | grep -v "discards 'const'" | \
grep '[0-9]\+:[0-9]\+: warning:'
< linter.out grep "Total errors found: [1-9]"
< linter.out grep 'total: [1-9]* errors'
< linter.out grep 'total: 0 errors' | grep '[1-9][0-9]* warnings'
} > style_summary.out
}

# Print grades: total, checker and style.
# Style grade is only awarded for assignments that have past 60 points
# of th checker grade.
print_results()
{
checker_grade=$(< checker.out sed -n '/^Checker:/s/^.*[ \t]\+\([0-9\.]\+\)\/.*$/\1/p')
if test "$(echo "$checker_grade > 60" | bc)" -eq 1; then
style_grade=$(cat style_grade.out)
else
style_grade=0
fi
final_grade=$(echo "scale=2; $checker_grade+$style_grade" | bc)
echo -e "\n\n### GRADE\n\n"
printf "Checker: %58s/ 90\n" "$checker_grade"
printf "Style: %60s/ 10\n" "$style_grade"
printf "Total: %60s/100\n" "$final_grade"

echo -e "\n\n### STYLE SUMMARY\n\n"
cat style_summary.out
}

run_interactive()
{
echo -e "\n\n### CHECKER\n\n"
stdbuf -oL make check 2>&1 | stdbuf -oL sed 's/^Total:/Checker:/g' | tee checker.out

echo -e "\n\n### LINTER\n\n"
stdbuf -oL make lint 2>&1 | tee linter.out

grade_style
print_results
}

run_non_interactive()
{
make check 2>&1 | sed 's/^Total:/Checker:/g' > checker.out
make lint > linter.out 2>&1

grade_style
print_results

echo -e "\n\n### CHECKER\n\n"
cat checker.out

echo -e "\n\n### LINTER\n\n"
cat linter.out
}

# In case of a command line argument disable interactive output.
# That is, do not show output as it generated.
# This is useful to collect all output and present final results at the
# beginning of the script output.
# This is because Moodle limits the output results, and the final results
# would otherwise not show up.
if test $# -eq 0; then
run_interactive
else
run_non_interactive
fi
Empty file.
Empty file.
Empty file.
Empty file.
1 change: 1 addition & 0 deletions content/assignments/elf-loader/tests/ref/no_pie_argc.ref
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
4
4 changes: 4 additions & 0 deletions content/assignments/elf-loader/tests/ref/no_pie_argv.ref
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
4
1
2
test
1 change: 1 addition & 0 deletions content/assignments/elf-loader/tests/ref/no_pie_auxv.ref
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0
1 change: 1 addition & 0 deletions content/assignments/elf-loader/tests/ref/no_pie_envp.ref
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ENV: test
1 change: 1 addition & 0 deletions content/assignments/elf-loader/tests/ref/no_pie_hello.ref
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello, world!
2 changes: 2 additions & 0 deletions content/assignments/elf-loader/tests/ref/nolibc.ref
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Hello, world!
Hello from rodata!
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello, .rodata text!
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello, .text test!
1 change: 1 addition & 0 deletions content/assignments/elf-loader/tests/ref/pie.ref
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello, world!
Loading