feat(pl): programming languages seminar#4

This commit is contained in:
maxbarsukov 2023-10-25 00:45:36 +03:00
parent a5f2fef094
commit 58ea41fe94
9 changed files with 332 additions and 0 deletions

View File

@ -0,0 +1,2 @@
*.o
hello

View File

@ -0,0 +1,13 @@
# Семинар 4: Виртуальная память, загрузка программ
- Что такое виртуальная память;
- Изучение карты памяти процесса;
- Использование `mmap`;
- Вызов ассемблерного кода из C;
- ELF-файлы;
- Загрузка файла;
[Семинар 4](https://gitlab.se.ifmo.ru/programming-languages/cse-programming-languages-fall-2023/main/-/tree/master/seminar-4)
[Задания](./exercises.md)

View File

@ -0,0 +1,163 @@
# Задания
### Вопрос
Посмотрите, какие у вас запущены процессы. Выберите один (например, оболочку, которой вы пользуетесь) и посмотрите на содержимое директории `/proc/PID/`, где `PID`его идентификатор.
### Вопрос
Что внутри файла `/proc/PID/environ`?
### Вопрос
Прочитайте про запуск программ в фоне. Что делают команды `bg`, `fg`, `jobs`?
Напишем [простую программу](./questions/q1.asm), которая входит в бесконечный цикл:
```nasm
section .data
correct: dq -1
section .text
global _start
_start:
jmp _start
```
Что она делает? Скомпилируйте и запустите её в фоне.
### Вопрос
Выведите с помощью `cat` содержимое файла `/proc/PID/maps`, где `PID` — идентификатор процесса, который вы запустили в фоне.
### Вопрос
Почему в регионах начальный и конечный адрес в 16-ричном формате заканчиваются всегда на три нуля?
### Вопрос
Определите, по каким адресам загружаются секции `.text` и `.data` из примера. Вам может помочь `readelf` и таблица символов.
### Вопрос
Определите хотя бы один запрещённый диапазон адресов.
### Вопрос
Что такое inode в файловой системе?
### Вопрос
Что находится в остальных столбцах? Прочитайте про файл `/proc/PID/maps` в `man procfs`.
### Вопрос
Прочитайте в `man mmap` ответы на следующие вопросы: Какие аргументы принимает `mmap`? В чём их смысл? Какой аргумент в каком регистре?
## Задание 1
Создайте файл `hello.txt` с текстом `Hello, mmap!`. Используя заготовку, отобразите его в память и выведите текст из него в стандартный поток вывода. Не забудьте вызвать `munmap` (его номер системного вызова `11`) и закрыть файл (`close`, номер системного вызова `3`) по завершению работы с файлом.
- [hello_mmap.asm](./task1/hello_mmap.asm)
[task1](./task1/)
## Задание 2
Прочитайте документацию по системному вызову `fstat` (номер `5`). Вас будет интересовать поле `st_size` типа `off_t` структуры `struct stat`, которую функция `fstat` заполняет. Используйте его, чтобы корректно вычислить размер файла при выводе данных, и выведите их, используя функцию `print_substring` (принимает на вход два аргумента: адрес начала строки и количество байт для вывода). Полученный размер файла используйте в вызовах `mmap`, `munmap` и `print_substring`.
Для выполнения задания вам пригодиться следующая информация:
```c
sizeof(struct stat) = 144
offsetof(struct stat, st_size) = 48
sizeof(off_t) = 8
```
Для выделения памяти на хранение структуры `stat` рекомендуется использовать стек.
[task2](./task2/)
### Вопрос
Что делает функция `world`? Объясните принцип её работы.
[string.asm](./questions/string.asm)
## Задание 3
В этих файлах не хватает нескольких строчек чтобы можно было взаимодействовать с кодом друг друга. Допишите файлы так, чтобы функции `print_string` и `hello` вызывалась и проверьте результат. Подсказка: вспомните, что нужно, чтобы из одного файла с C-кодом вызвать код из другого файла.
[task3](./task3/)
### Вопрос
Какие аргументы может принимает функция языка С по умолчанию? В чем отличие прототипов `int f();` и `int f(void);`?
### Вопрос
Изучите `Makefile` [для сборки этой программы](./makefile). Что означают `$@`, `$^`?
- `$@` - цель (имя файла для сборки) текущего правила.
- `$^` - список всех зависимостей текущего правила, разделенных пробелами. Повторные включения зависимостей удаляются (каждая зависимость включается в список ровно один раз).
### Вопрос
Что такое секции `.rodata` и `.bss`?
### Вопрос
Что в `nasm` делают директивы `resb`, `resq` и [другие](https://nasm.us/doc/nasmdoc3.html)?
### Вопрос
В файле `hello.c`, в какой секции будет выделена строка `=”hello”=`?
### Вопрос
Введите `readelf` без аргументов. Прочитайте вывод и определите, какие ключи необходимы, чтобы отобразить три заголовка файла.
### Вопрос
Выведите `file header` (ключ `-h` или `--file-header`) для файла `hello.o`. Что такое `Entry point address` и почему его значение `0`?
### Вопрос
Выведите `program header` (ключ `-l` или `--program-headers`) для файла `hello.o`. Объясните результат.
### Вопрос
Определите адреса загрузки секций `.text` и `.rodata` используя список секций (ключ `-S` или `--section-headers`).
### Вопрос
Выведите `program header` для файла `hello`. В какие сегменты попадают `.text` и `.rodata`? Какие адреса загрузки этих сегментов?
### Вопрос
Модифицируйте программу на C так, чтобы она входила в бесконечный цикл. Это позволит нам проверить наши догадки про адреса секций и сегментов.
### Вопрос
Выведите карту регионов памяти для запущенной программы, с которой мы сегодня работаем. Сопоставьте сегменты, содержащие секции `.rodata` и `.text` регионам памяти. Верно ли, что одному региону памяти соответствует только один сегмент?
### Вопрос
Какие регионы соответствуют частям динамических библиотек? У динамических библиотек в Linux расширение `*.so`.
### Вопрос
Выполните `ldd hello`. Объясните результат.
### Вопрос
Как вы думаете, почему стандартная библиотека C реализована как динамическая библиотека, а не включается в состав исполняемого файла статически, как обычные `.o` файлы?
## Задание 4
Объедините ассемблерный код для вывода содержимого файла с кодом на языке C. Пусть ваша программа будет просить пользователя ввести название файла, а затем выведет его содержимое в стандартный поток вывода используя код, написанный в начале сегодняшнего семинара (сделайте из него функцию `print_file`, которая будет принимать имя файла первым аргументом). Не забудьте, что для корректной работы необходимо следовать соглашениям о вызовах и сохранить `callee-saved` регистры, которые вы используете, в начале своей функции `print_file`. Для вывода сообщений (например `“Please enter file name: “`) используйте собственную реализацию `print_string` из сегодняшнего семинара.
Желающие могут прочитать больше про регионы `[vdso]` и `[vvar]`, использующиеся для ускорения некоторых системных вызовов:
- `man vdso`
- [Implementing virtual system calls](https://lwn.net/Articles/615809/)
[task4](./task4/)

View File

@ -0,0 +1,5 @@
/* hello.c */
int main() {
print_string("hello");
world();
}

View File

@ -0,0 +1 @@
Hello, mmap!

View File

@ -0,0 +1,23 @@
CFLAGS = -g -O2 -Wall -Werror -std=c17 -Wdiscarded-qualifiers -Wincompatible-pointer-types -Wint-conversion
CC = gcc
LD = gcc
ASM = nasm
ASMFLAGS =-felf64 -g
# Если есть код на C, то компоновку тоже нужно производить
# с помощью gcc, а не ld
hello: hello.o string.o
$(LD) -o $@ $^
# target : dependencies
# do this
hello.o: hello.c
$(CC) -c $(CFLAGS) -o $@ $<
string.o: string.asm
$(ASM) $(ASMFLAGS) -o $@ $<
clean:
$(RM) hello hello.o string.o
.PHONY: clean

View File

@ -0,0 +1,6 @@
section .data
correct: dq -1
section .text
global _start
_start:
jmp _start

View File

@ -0,0 +1,38 @@
; string.asm
global world
global print_string
section .rodata
message: db ", world!", 10, 0
section .text
string_length:
mov rax, 0
.loop:
xor rax, rax
.count:
cmp byte [rdi+rax], 0
je .end
inc rax
jmp .count
.end:
ret
world:
mov rdi, message
print_string:
push rax
push rdi
call string_length
mov rsi, [rsp]
mov rdx, rax
mov rax, 1
mov rdi, 1
push rcx
syscall
pop rcx
pop rdi
pop rax
ret

View File

@ -0,0 +1,81 @@
; hello_mmap.asm
%define O_RDONLY 0
%define PROT_READ 0x1
%define MAP_PRIVATE 0x2
%define SYS_WRITE 1
%define SYS_OPEN 2
%define SYS_MMAP 9
%define SYS_EXIT 60
%define FD_STDOUT 1
section .data
; This is the file name. You are free to change it.
fname: db 'hello.txt', 0
section .text
global _start
; use exit system call to shut down correctly
exit:
mov rax, SYS_EXIT
xor rdi, rdi
syscall
; These functions are used to print a null terminated string
; rdi holds a string pointer
print_string:
push rdi
call string_length
pop rsi
mov rdx, rax
mov rax, SYS_WRITE
mov rdi, FD_STDOUT
syscall
ret
string_length:
xor rax, rax
.loop:
cmp byte [rdi+rax], 0
je .end
inc rax
jmp .loop
.end:
ret
; This function is used to print a substring with given length
; rdi holds a string pointer
; rsi holds a substring length
print_substring:
mov rdx, rsi
mov rsi, rdi
mov rax, SYS_WRITE
mov rdi, FD_STDOUT
syscall
ret
_start:
; Вызовите open и откройте fname в режиме read only.
mov rax, SYS_OPEN
mov rdi, fname
mov rsi, O_RDONLY ; Open file read only
mov rdx, 0 ; We are not creating a file
; so this argument has no meaning
syscall
; rax holds the opened file descriptor now
; Вызовите mmap c правильными аргументами
; Дайте операционной системе самой выбрать, куда отобразить файл
; Размер области возьмите в размер страницы
; Область не должна быть общей для нескольких процессов
; и должна выделяться только для чтения.
; с помощью print_string теперь можно вывести его содержимое
; теперь можно освободить память с помощью munmap
; закрыть файл используя close
; и выйти
call exit