mirror of
https://github.com/maxbarsukov/itmo.git
synced 2026-09-12 19:19:57 +05:00
feat(pl): ✨ programming languages seminar#4
This commit is contained in:
parent
a5f2fef094
commit
58ea41fe94
2
3 япы/семинары/seminar4/.gitignore
vendored
Normal file
2
3 япы/семинары/seminar4/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.o
|
||||
hello
|
||||
13
3 япы/семинары/seminar4/README.md
Normal file
13
3 япы/семинары/seminar4/README.md
Normal 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)
|
||||
163
3 япы/семинары/seminar4/exercises.md
Normal file
163
3 япы/семинары/seminar4/exercises.md
Normal 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/)
|
||||
5
3 япы/семинары/seminar4/hello.c
Normal file
5
3 япы/семинары/seminar4/hello.c
Normal file
@ -0,0 +1,5 @@
|
||||
/* hello.c */
|
||||
int main() {
|
||||
print_string("hello");
|
||||
world();
|
||||
}
|
||||
1
3 япы/семинары/seminar4/hello.txt
Normal file
1
3 япы/семинары/seminar4/hello.txt
Normal file
@ -0,0 +1 @@
|
||||
Hello, mmap!
|
||||
23
3 япы/семинары/seminar4/makefile
Normal file
23
3 япы/семинары/seminar4/makefile
Normal 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
|
||||
6
3 япы/семинары/seminar4/questions/q1.asm
Normal file
6
3 япы/семинары/seminar4/questions/q1.asm
Normal file
@ -0,0 +1,6 @@
|
||||
section .data
|
||||
correct: dq -1
|
||||
section .text
|
||||
global _start
|
||||
_start:
|
||||
jmp _start
|
||||
38
3 япы/семинары/seminar4/string.asm
Normal file
38
3 япы/семинары/seminar4/string.asm
Normal 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
|
||||
81
3 япы/семинары/seminar4/task1/hello_mmap.asm
Normal file
81
3 япы/семинары/seminar4/task1/hello_mmap.asm
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user