Вы не можете использовать расширенный asm в функции naked
, только базовый c asm, согласно инструкции g cc . Вам не нужно сообщать компилятору о засоренных регистрах (поскольку он все равно ничего с ними не сделает; в функции naked
вы отвечаете за все управление регистрами). И передача адреса entry
в расширенном операнде не нужна; просто сделайте jmp entry
.
(В моих тестах ваш код вообще не компилируется, поэтому я предполагаю, что вы не указали нам свой точный код - в следующий раз, пожалуйста, сделайте это, чтобы не тратить время людей) .)
Linux x86-64 syscall
Системным вызовам разрешено блокировать регистры rcx
и r11
, поэтому вам необходимо добавить их в списки блокирующих операторов. ваши системные вызовы.
Вы выравниваете стек по 16-байтовой границе, прежде чем перейти к entry
. Однако 16-байтовое правило выравнивания основано на предположении, что вы будете вызывать функцию с call
, что приведет к добавлению sh дополнительных 8 байтов в стек. Таким образом, вызываемая функция фактически ожидает, что стек изначально будет не кратным 16, а 8 большим или меньшим, чем кратное 16. Таким образом, вы на самом деле неправильно выравниваете стек, и это может быть причиной всех видов ошибок. таинственная проблема.
Так что либо замените ваш jmp
на call
, либо вычтите еще 8 байтов из rsp
(или просто push
какой-нибудь 64-битный регистр на ваш выбор).
Примечание по стилю: unsigned long
- это уже 64 бита на Linux x86-64, поэтому было бы более логично c использовать его вместо unsigned long long
везде.
Общая подсказка: узнайте об ограничениях регистра в расширенном asm. Вы можете заставить компилятор загружать нужные вам регистры вместо того, чтобы писать инструкции в ассемблере, чтобы сделать это самостоятельно. Таким образом, ваша exit
функция может выглядеть следующим образом:
void exit(unsigned long status) {
asm volatile("syscall"
: //no outputs
:"a"(60), "D" (status)
:"rcx", "r11");
}
Это, в частности, экономит вам несколько инструкций, поскольку status
уже находится в регистре %rdi
при входе в функцию , С вашим исходным кодом компилятор должен переместить его куда-то еще, чтобы вы могли загрузить его в %rdi
самостоятельно.
Ваша функция open
всегда возвращает 1, что, как правило, не будет фактически открывшимся fd. Поэтому, если ваша программа запускается со стандартным перенаправленным выводом, ваша программа будет записывать в перенаправленный стандартный вывод, а не в tty, как это, кажется, хочет делать. В самом деле, это делает системный вызов open
совершенно бессмысленным, потому что вы никогда не используете открываемый файл.
Вы должны организовать open
для возврата значения, которое фактически было возвращено системным вызовом, что будет осталось в регистре %rax
, когда syscall
вернется. Вы можете использовать выходной операнд, чтобы сохранить его во временной переменной (которую компилятор, скорее всего, оптимизирует), и вернуть ее. Вам нужно будет использовать ограничение di git, поскольку оно входит в тот же регистр, что и входной операнд. Я оставляю это как упражнение для вас. Было бы также неплохо, если бы ваша функция write
действительно возвращала количество записанных байтов.