Пролог сгенерированных функций ARM GCC
Я упоминал, что наборы инструментов ARM могут генерировать прологи различных функций. На самом деле, я видел два файла obj (vmlinux) с совершенно разными прологами функций:
Первый случай выглядит так:
push {some registers maybe, fp, lr} (lr ommited in leaf function)
Второй случай выглядит так:
push {some registers maybe, fp, sp, lr, pc} (i can confuse the order)
Так что, как я вижу, второй толкает дополнительно пк и зр. Также я видел некоторые комментарии в утилите аварийного отключения (проект kdump), где было указано, что стек-кадр ядра должен иметь формат {..., fp, sp, lr, pc}, что меня больше смущает, потому что я вижу, что в некоторых случаях это не так правда.
1.) Прав ли я, что некоторые дополнительные флаги gcc необходимы для добавления дополнительных функций pc и sp в пролог функции? Если да, то что они?
2.) Для чего это используется? В принципе, как я понимаю, я могу раскрутить стек только с FP и LR, зачем мне эти дополнительные значения?
3.) Если это ничего не мешает с флагами компиляции - как я могу форсировать генерацию этого пролога расширенной функции и снова, какова цель?
Спасибо.
1 ответ
1.) Прав ли я, что некоторые дополнительные флаги gcc необходимы для добавления дополнительных функций pc и sp в пролог функции? Если да, то что они?
Есть много опций gcc, которые влияют на кадры стека (-march
, -mtune
и т. д. может повлиять на инструкции, используемые, например). В вашем случае это было -mapcs-frame
, Также, -fomit-frame-pointer
удалит кадры из конечных функций. Несколько статических функций могут быть объединены в одну сгенерированную функцию, что дополнительно уменьшает количество кадров. APCS может вызвать немного более медленный код, но необходим для трассировки стека.
2.) Для чего это используется? В принципе, как я понимаю, я могу раскрутить стек только с FP и LR, зачем мне эти дополнительные значения?
Все регистры, которые не являются параметрами (r0-r3), должны быть сохранены, поскольку они должны быть восстановлены при возврате к вызывающей стороне. Компилятор выделит дополнительные локальные элементы в стеке так, sp
будет почти всегда меняться, когда fp
изменения. Почему pc
хранится, см. ниже.
3.) Если это ничего не мешает с флагами компиляции - как я могу форсировать генерацию этого пролога расширенной функции и снова, какова цель?
Это флаги компилятора, как вы уже догадались.
; Prologue - setup
mov ip, sp ; get a copy of sp.
stm sp!, {fp, ip, lr, pc} ; Save the frame on the stack. See Addendum
sub fp, ip, #4 ; Set the new frame pointer.
...
; Epilogue - return
ldm sp, {fp, sp, lr} ; restore stack, frame pointer and old link.
... ; maybe more stuff here.
bx lr ; return.
Типичное сохранение stm sp!, {fp, ip, lr, pc}
и восстановление ldm sp, {fp, sp, lr}
, Это правильно, если вы изучаете документы ABI/APCS. Обратите внимание, что нет! чтобы попытаться исправить стек. Загружается явно из сохраненного ip
значение.
Также сохранены pc
не используется в эпилоге. Это просто сброшенные данные в стеке. Так зачем это делать? Обработчики исключений (прерывания, сигналы или исключения C++) и другие механизмы трассировки стека хотят знать, кто сохранил кадр. ARM всегда имеет только один пролог функции (одна точка входа). Однако есть несколько выходов. В некоторых случаях возврат, как return function();
может фактически превратиться в b function
в, возможно, больше вещей здесь. Это известно как хвостовой вызов. Также, когда листовая функция вызывается в середине процедуры и возникает исключение, она увидит PC
диапазон листа, но лист может не иметь рамки вызова. Сохраняя pc
, кадр вызова может быть исследован, когда в листе возникает исключение, чтобы узнать, кто действительно спас стек. Таблицы pc
по сравнению с деструктором и т. д. может храниться, чтобы позволить объектам быть освобожденными или выяснить, как вызвать обработчик сигнала. Экстра pc
это просто замечательно при трассировке стека, и операция почти бесплатна из-за футеровки трубы.
Смотрите также: ARM Link и фрейм регистров вопрос о том, как компилятор использует эти регистры.