Написание игрушечного компилятора - вызов не работает - PullRequest
1 голос
/ 10 июля 2020

Этот вопрос возникает из-за того, что я пишу игрушечный компилятор, но я не думаю, что нужно показывать компиляторную сторону этого вопроса - это строго проблема, потому что я не очень разбираюсь в сборке.

Я работаю над реализацией функций на моем языке. Я создал следующую сборку (AT&T), которая должна напечатать 6. Ошибка программы по адресу call *%rax, что, как я полагаю, означает, что я неправильно сохраняю адрес своей функции. Что должно произойти, так это то, что функция f0 должна прочитать аргументы из стека, умножить их и оставить результат в %rax. Функция print - это просто оболочка вокруг функции printf из C для печати %rax.

Я запускаю Ubuntu 20.04, и код был скомпилирован с gcc test.S -o test.out -no-pie, на случай это имеет значение.

.data
numfmt:
.asciz "%d\n"

.text
print:
push %rbx
mov %rax, %rsi
mov $numfmt, %rdi
xor %rax, %rax
call printf
pop %rbx
ret

.globl main
main:
push %rbp
mov %rsp, %rbp
jmp s0
f0:
push %rbp
mov %rsp, %rbp
mov 16(%rbp), %rax
push %rax
mov 8(%rbp), %rax
pop %rcx
imul %rcx, %rax
mov %rbp, %rsp
pop %rbp
ret
s0:
mov $f0, %rax
push %rax
mov $1, %rax
push %rax
mov $2, %rax
push %rax
mov -8(%rbp), %rax
call *%rax
add 16, %rsp
call print
mov $0, %rax
mov %rbp, %rsp
pop %rbp
ret

1 Ответ

2 голосов
/ 10 июля 2020

В синтаксисе AT&T mov f0, %rax является косвенным перемещением: он загружает %rax с qword, расположенным по адресу f0. Но вы хотите загрузить сам адрес. Это немедленный ход, и поэтому перед операндом f0 необходимо добавить префикс $, чтобы указать, что он немедленный. Таким образом, он должен сказать mov $f0, %rax.

По тем же причинам, add 16, %rsp неверно, поскольку он пытается добавить к %rsp значение, расположенное по адресу 16. Эта страница не отображается, следовательно, segfault. Опять же, вы хотите add $16, %rsp.

Далее, в f0 смещения указателя кадра неверны. 16(%rbp) - это второй передаваемый параметр, а не первый, а 8(%rbp) - это адрес возврата. Не забудьте учесть влияние самого push %rbp на указатель стека. Таким образом, вы в конечном итоге вычисляете 2-кратный адрес возврата вместо 1-го 2. Сделайте эти 24(%rbp) и 16(%rbp).

Наконец, вам нужно убедиться, что стек выровнен по 16 байтам при вызове printf. В противном случае библиотечные функции ведут себя непредсказуемо. Иногда случается, что они не делают ничего, что требует согласования, и им кажется, что все работает; в других случаях они могут взломать sh.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...