Pintos - проект системных вызовов 2
Я делаю проект Pintos на стороне, чтобы узнать больше об операционных системах. Я закончил Проект 1 и начал второй проект. У меня уже есть проверенный и работающий стек установки (через hex_dump). Сейчас у меня проблемы с получением правильных аргументов системного вызова.
В user/syscall.c есть 4 сборочных заглушки (от 0 до 4 заглушек), которые переносит пользователь syscall.
#define syscall3(NUMBER, ARG0, ARG1, ARG2) \
({ \
int retval; \
asm volatile \
("pushl %[arg2]; pushl %[arg1]; pushl %[arg0]; " \
"pushl %[number]; int $0x30; addl $16, %%esp" \
: "=a" (retval) \
: [number] "i" (NUMBER), \
[arg0] "g" (ARG0), \
[arg1] "g" (ARG1), \
[arg2] "g" (ARG2) \
: "memory"); \
retval; \
}) (this code is given to us)
У меня есть некоторый код внутри моего syscall_handler, который вызывает правильную функцию внутри ядра.
static void syscall_handler (struct intr_frame *f) {
uint32_t *args = f->esp;
if (args[0] == SYS_WRITE) {
f->eax = write(args);
}
Внутри моей функции записи я печатаю FD и размер
int sysCallNumber = (int) args[0];
int fd = (int) args[1];
const char *buffer = (char *) args[2];
unsigned size = (unsigned) args[3];
printf("FD is %d\n", fd);
printf("Size is %d\n", size);
Запуск 'echo hello stack overflow 1 22 333' даст следующий результат. Примечание Я добавил примечания в скобках. () <- что-то напортачило и FD переопределяется с размером (включая нулевой терминатор)
FD is 6 (hello)
Size is 6
FD is 6 (stack)
Size is 6
FD is 9 (overflow)
Size is 9
FD is 2 (1)
Size is 2
FD is 3 (22)
Size is 3
FD is 4 (333)
Size is 4
FD is 1 (this is from the printf("\n") in echo.c)
Size is 1
Я управлял этим с GDB, устанавливая точки останова и сбрасывая фреймы, и не смог понять это. Кто-нибудь сталкивался с чем-нибудь подобным? Если да, то как ты это исправил?
Спасибо!
1 ответ
Я недавно обнаружил ошибку в user/syscall.c
это может повлиять на вас. Попробуйте применить этот патч к user/syscall.c
:
diff --git a/src/lib/user/syscall.c b/src/lib/user/syscall.c
index a9c5bc8..efeb38c 100644
--- a/src/lib/user/syscall.c
+++ b/src/lib/user/syscall.c
@@ -10,7 +10,7 @@
("pushl %[number]; int $0x30; addl $4, %%esp" \
: "=a" (retval) \
: [number] "i" (NUMBER) \
- : "memory"); \
+ : "memory", "esp"); \
retval; \
})
@@ -24,7 +24,7 @@
: "=a" (retval) \
: [number] "i" (NUMBER), \
[arg0] "g" (ARG0) \
- : "memory"); \
+ : "memory", "esp"); \
retval; \
})
@@ -40,7 +40,7 @@
: [number] "i" (NUMBER), \
[arg0] "g" (ARG0), \
[arg1] "g" (ARG1) \
- : "memory"); \
+ : "memory", "esp"); \
retval; \
})
@@ -57,7 +57,7 @@
[arg0] "g" (ARG0), \
[arg1] "g" (ARG1), \
[arg2] "g" (ARG2) \
- : "memory"); \
+ : "memory", "esp" ); \
retval; \
})
Вот длинная история...
В макросе syscall3
например, ожидаемый ассемблерный код будет выглядеть примерно так
pushl ARG2
pushl ARG1
pushl ARG0
pushl NUMBER
int $0x30
addl $16, %esp
Это должно работать до тех пор, пока стек выглядит точно так, как ожидается, просто нажмите на инструкцию прерывания. Вот то, что разобрали write
функция выглядит как в тестовых программах (генерируется objdump -d pintos/src/userprog/build/tests/userprog/args-many
):
0804a1a5 <write>:
804a1a5: ff 74 24 0c pushl 0xc(%esp)
804a1a9: ff 74 24 08 pushl 0x8(%esp)
804a1ad: ff 74 24 04 pushl 0x4(%esp)
804a1b1: 6a 09 push $0x9
804a1b3: cd 30 int $0x30
804a1b5: 83 c4 10 add $0x10,%esp
804a1b8: c3 ret
Существует огромная проблема с этими инструкциями. Потому что аргументы помещаются в стек относительно указателя стека %esp
неправильные аргументы помещаются в стек! Это потому что %esp
меняется после каждого pushl
инструкция.
После некоторого изучения расширенного синтаксиса asm для gcc, я думаю, что нашел правильное решение. %esp
регистр должен быть добавлен в список Clobbers в расширенной инструкции asm для каждого syscall*
, Это потому, что каждый pushl
инструкция изменяет %esp
косвенно, поэтому мы должны сообщить gcc об этой модификации, чтобы она не использовала %esp
неправильно в сгенерированных инструкциях.