- Игрушечный интерпретатор
- Подмножество find
- Кусочек JIT компилятора
- Знакомство с библиотеками
- Знакомство с сокетами
- Знакомство с передачей дескрипторов и IPC
- Обработка сигналов
Программа должна в бесконечном цикле считывать с stdin полный путь к исполняемому файлу, который необходимо запустить и аргументы запуска. Дождавшись завершения процесса необходимо вывести на stdout код его завершения.
Необходимо использовать прямые системные вызовы для порождения новых процессов, запуска новых исполняемых файлов и получения статуса завершения системного вызова.
Все возвращаемые значения системных вызовов должны быть проверены и в случае обнаружения ошибок необходимо выводить текстовое описание ошибки.
На входе могут быть некорректные данные.
Дополнительные баллы - поддержка переменных окружения.
Язык имплементации - C или C++.
- Первым аргументом принимать абсолютный путь, в котором будет производиться поиск файлов.
- По-умолчанию выводить в стандартный поток вывода все найденные файлы по этому пути
- Поддерживать аргумент -inum num. Аргумент задает номер инода
- Поддерживать аргумент -name name. Аргумент задает имя файла
- Поддерживать аргумент -size [-=+]size. Аргумент задает фильтр файлов по размеру(меньше, равен, больше)
- Поддерживать аргумент -nlinks num. Аргумент задает количество hardlink'ов у файлов
- Поддерживать аргумент -exec path. Аргумент задает путь до исполняемого файла, которому в качестве единственного аргумент нужно передать найденный в иерархии файл
- Поддерживать комбинацию аргументов. Например хочется найти все файлы с размером больше 1GB и скормить их утилите /usr/bin/sha1sum.
- Выполнять поиск рекурсивно, в том числе во всех вложенных директориях.
- Сильные духом призываются к выполнению задания с использованием системного вызова getdents(2). Остальные могут использовать readdir и opendir для чтения содержимого директории.
Цель - получить знакомство с системными вызывами, используемыми для получения/освобождения памяти от ядра. Получить представление о том, как может работать JIT компилятор.
- Выделить память с помощью mmap(2).
- Записать в выделенную память машинный код, соответсвующий какой-либо функции.
- Изменить права на выделенную память - чтение и исполнение. See: mprotect(2).
- Вызвать функцию по указателю на выделенную память.
- Освободить выделенную память.
- man objdump
- help disassemble в gdb
Сильные духом призываются к возможности модификации кода выполняемой функции в runtime. Например, вы можете получить аргументом вызова вашей программы какое-то число и пропатчить машинный код этим числом. Эта часть задания будет оцениваться в дополнительные баллы.
Необходимо создать статическую, и две динамических библиотеки и программу, которая будет их использовать. Помимо этого нужен Makefile(либо другой инструмент автоматизации сборки), с помощью которого можно будет собрать все части.
- Собираться статически
- Предоставлять какие-то функции
- Собираться динамически
- Динамически линковаться с программой на этапе линковки
- Предоставлять какие-то функции
- Собираться динамически
- Предоставлять какие-то функции
- Статически линковаться с статической библиотекой и вызывать предоставляемые ей функции
- Динамически линковаться с первой динамической библиотекой и вызывать предоставляемые ей функции
- Во время выполнения в явном виде загружать вторую динамическию библиотеку с помощью dlopen(3) и вызывать какие-то функции из нее
- man dlopen(3), man ld(1), man gcc(1)
Необходимо попробовать клиент-серверное взаимодействие через синхронные сокеты. Помимо этого нужен Makefile, с помощью которого можно будет собрать клиент и сервер. Семейство протоколов для использования на выбор: AF_UNIX, AF_INET, AF_INET6.
- В качестве аргументов принимать адрес, на котором будет ожидать входящих соединений
- Стартовать, делать bind(2) на заданный адрес и ожидать входящих соединений
- При получении соединения, выполнять серверную часть придуманного вами протокола
- После обработки принятого соединения возвращаться в режим ожидания входящих соединений
- Принимать параметром адрес, к которому стоит подключиться
- Выполнять клиентскую часть придуманного вами протокола
- Завершаться
Для сильных духом предлагается выбрать какой-то существующий протокол и имплементировать его, или его разумное подмножество. Сильность духа будет оцениваться в два балла, при условии что выбранный протокол сложнее чем ECHO (https://tools.ietf.org/html/rfc862).
Необходимо получить опыт работы с IPC. Нужно создать приложение клиента и сервера.
- Клиент и сервер обшаются через UNIX сокет
- Клиент подключается к серверу через UNIX сокет, получает от сервера файловый дескриптор, соответсвующий объекту какого-либо вида IPC
- Клиент и сервер выполняют какое-то взаимодействие используя IPC
- Ожидать подключений на UNIX сокете
- Для новых соединений создавать новый вид IPC, объекты которого представимы в виде файловых дескрипторов
- Передавать через UNIX сокет клиенту файловый дескриптор IPC соответсвующий клиенту
- Ожидать выполнения какого-либо протокола поверх IPC с клиентом
- Подключиться на UNIX сокет к серверу
- Получать в виде файлового дескриптора клиентскую часть IPC
- Взаимодействовать с сервером через IPC для выполнения какого-либо протокола
Примерами IPC могут служить: PIPE, socket, файловые дескрипторы для разделямой памяти или сообщений(POSIX), файловые дескрипторы для анонимной памяти.
- man 7 unix
- Дампить значения general purpose регистров, соответствуюших моменту падения
- Дампить память поблизости от адреса, по которому произошло нарушение защиты памяти
- Адрес, по которому был сгенерирован SIGSEGV - NULL
- Адрес, по которому был сгенерирован SIGSEGV - находится на границе валидной памяти и нет
- man 2 sigaction
- man 2 getcontext