Функции листьев в SPARC и HP-UX

Я только что читал статью Phrack, в которой был разбор в HP-UX. Я читал, что есть два класса функций, которые возможны в HP-UX и SPARC; листовые и нелистовые функции. Ниже приведен фрагмент разборки, который я взял здесь.

(gdb) disass leaf
Dump of assembler code for function foo:
0x3280 <leaf>:           copy r3,r1
0x3284 <leaf+4>:         copy sp,r3
0x3288 <leaf+8>:         stw,ma r1,40(sr0,sp)
0x328c <leaf+12>:        stw r26,-24(sr0,r3)
0x3290 <leaf+16>:        stw  r0,8(sr0,r3)
0x3294 <leaf+20>:        ldi 1,r19
0x3298 <leaf+24>:        stw  r19,8(sr0,r3)
0x329c <leaf+28>:        ldo 40(r3),sp
0x32a0 <leaf+32>:        ldw,mb -40(sr0,sp),r3
0x32a4 <leaf+36>:        bv,n r0(rp)
End of assembler dump.
(gdb)

Обычно при вызове функции адрес возврата помещается в стек, чтобы программа знала, куда вернуть управление после завершения выполнения функции. Как это работает в случае этих листовых функций?

У меня нет доступа к машинам HP-UX/SPARC, поэтому я не могу попробовать это самостоятельно (и я не очень хорошо понимаю сборку в этом случае по той же причине).

Может ли кто-нибудь объяснить, как в этом случае управление возвращается к вызываемой функции?


person Community    schedule 30.03.2011    source источник


Ответы (1)


Во-первых, документ, на который вы ссылаетесь, и код, который вы показываете, относятся не к Sparc, а к PA-RISC, который представляет собой отдельную архитектуру. Насколько я знаю, нет версии HP/UX, которая работает на системах на основе Sparc.

Тем не менее, вопрос о листовых функциях одинаков во многих архитектурах, включая Sparc, PA-RISC, PowerPC, ARM, MIPS... фактически во всех архитектурах RISC. Во всех случаях код операции, выполняющий вызов функции, не сохраняет адрес возврата в стеке; на самом деле не существует «настоящего» стека, известного аппаратному обеспечению. Вместо этого существует просто программное соглашение об использовании определенного регистра в качестве указателя стека. Вызывающий код операции сохраняет возвращенный адрес в определенном регистре, обычно называемом «реестр связи». Код операции для возврата из функции просто считывает этот регистр.

Если сама функция (назовем ее A) вызывает другую функцию (B), то этот вложенный вызов также будет использовать регистр связи; однако A потребуется содержимое регистра связи, когда он вернется. Следовательно, A должен где-то сохранить регистр связи, обычно в памяти, а точнее в области памяти, обычно используемой как «стек».

Листовая функция — это функция, которая не вызывает никакой другой функции; он просто делает свою работу и возвращается. Листовой функции не нужно сохранять регистр связи в стеке, поскольку ничто не изменит содержимое регистра связи.

Дополнительным ограничением, которое также является обычным, является структура кадра стека. Некоторые операционные системы на некоторых архитектурах настаивают на том, чтобы функции использовали стек очень специфическим образом (так называемые «фреймы стека»), которые могут быть надежно исследованы отладчиками. Затем спецификация требует сохранения адреса возврата в определенной ячейке стека, и вызываемая функция должна сделать это на самом раннем этапе своего выполнения. Затем те же операционные системы могут заявить, что это не обязательно для листовых функций (отладчику легче иметь дело с функцией без фреймов, если эта функция не вызывает ничего другого, т. тот, которого не хватает). Отсутствие настройки кадра стека более эффективно (по скорости выполнения и размеру кода), чем настройка кадра стека, отсюда и исключение.

person Thomas Pornin    schedule 30.03.2011