При компиляции кода C и просмотре сборки, все это имеет стек, растущий в обратном направлении, вот так:
_main:
pushq %rbp
movl $5, -4(%rbp)
popq %rbp
ret
-4(%rbp)
- означает ли это, что базовый указатель или указатель стека фактически перемещаются вниз по адресам памяти, а не идут вверх? Почему это?
Я изменил $5, -4(%rbp)
на $5, +4(%rbp)
, скомпилировал и запустил код, и ошибок не было. Так почему же мы все еще должны идти назад в стеке памяти?
-4(%rbp)
базовый указатель вообще не перемещается, и это+4(%rbp)
не может сработать.malloc
растет куча назадmain
что его RBP будет перегружен. Это конечно возможно. (И да, запись4(%rbp)
будет наступать на сохраненное значение RBP). На самом деле, это правило никогда не выполняетсяmov %rsp, %rbp
, поэтому доступ к памяти зависит от RBP вызывающего , если это именно то, что на самом деле проверял OP !!! Если это было фактически скопировано из вывода компилятора, некоторые инструкции были опущены!Ответы:
Да,
push
инструкции уменьшают указатель стека и записывают в стек, в то время какpop
выполняют обратное чтение из стека и увеличивают указатель стека.Это несколько исторично, поскольку для машин с ограниченной памятью стек размещался высоко и увеличивался вниз, а куча - низко и увеличивалась вверх. Существует только один пробел «свободной памяти» - между кучей и стеком, и этот пробел распределяется между собой, либо любой может вырасти в промежуток по мере необходимости. Таким образом, программе не хватает памяти только тогда, когда стек и куча сталкиваются, не оставляя свободной памяти.
Если стек и куча растут в одном и том же направлении, то есть два пробела, и стек действительно не может врасти в промежуток кучи (наоборот, это также проблематично).
Первоначально процессоры не имели специальных инструкций по обработке стека. Однако, поскольку поддержка аппаратного обеспечения была добавлена к оборудованию, она приобрела тенденцию к снижению, и процессоры все еще следуют этой схеме сегодня.
Можно утверждать, что на 64-битной машине достаточно адресного пространства, чтобы разрешить множественные промежутки - и, как доказательство, множественные промежутки обязательно имеют место, когда процесс имеет несколько потоков. Хотя это не является достаточной мотивацией для изменения ситуации, поскольку в случае систем с множеством разрывов направление роста, возможно, произвольно, поэтому традиции / совместимость перевешивают масштабы.
Вы должны были бы изменить инструкции по обработке стеки CPU для того , чтобы изменить направление стека, либо отказаться от использования выделенного толкая и выскакивают инструкции (например
push
,pop
,call
,ret
и других).Обратите внимание, что архитектура набора команд MIPS не имеет выделенного символа
push
&pop
, поэтому практично увеличивать стек в любом направлении - вам все еще может потребоваться размещение памяти с одним зазором для однопоточного процесса, но можно увеличить стек вверх и кучу вниз. Однако, если вы это сделаете, для некоторого кода C varargs может потребоваться корректировка в передаче исходных или скрытых параметров.(На самом деле, поскольку в MIPS нет выделенной обработки стека, мы можем использовать предварительное или последующее увеличение или предварительное или последующее уменьшение для добавления в стек до тех пор, пока мы используем точную обратную операцию для выталкивания из стека, а также при условии, что операционная система соответствует выбранной модели использования стека. Действительно, в некоторых встроенных системах и некоторых образовательных системах стек MIPS растет.)
источник
push
иpop
на большинстве архитектур, но и гораздо более важное прерывание обработку,call
,ret
и все остальное испекло-во взаимодействии со стеком.gets()
или делаетstrcpy()
его без встроенного, то при возврате в этих библиотечных функциях будет использоваться перезаписанный адрес возврата. В настоящее время с растущими вниз стеками, это когда их вызывающий возвращается.strcpy
), в дуге, где адрес возврата хранится в регистре, если только он не должен быть разлит, нет доступа к clobber возврата адрес.В вашей конкретной системе стек начинается с высокого адреса памяти и «растет» вниз до низкого адреса памяти. (симметричный случай от низкого до высокого также существует)
И так как вы изменили с -4 и +4, и он побежал, это не значит, что это правильно. Структура памяти работающей программы более сложна и зависит от многих других факторов, которые могут способствовать тому, что вы не сразу потерпели крах в этой чрезвычайно простой программе.
источник
Указатель стека указывает на границу между выделенной и нераспределенной стековой памятью. Увеличение этого значения означает, что оно указывает на начало первой структуры в выделенном стековом пространстве, а другие выделенные элементы следуют по более крупным адресам. Наличие указателей на начало выделенных структур встречается гораздо чаще, чем наоборот.
Сейчас во многих системах в наши дни существует отдельный регистр для стековых фреймов, который можно несколько надежно размотать, чтобы выяснить цепочку вызовов с распределением хранилища локальных переменных. Путь этот стек регистр кадров устанавливается на некоторых архитектурах означает , что он заканчивает наведение за хранение локальной переменной , в отличие от указателя стека перед ним. Таким образом, использование этого регистра стека кадров требует отрицательной индексации.
Обратите внимание, что стековые фреймы и их индексация являются побочным аспектом скомпилированных компьютерных языков, поэтому именно генератор кода компилятора должен иметь дело с «неестественностью», а не с плохим программистом на ассемблере.
Таким образом, хотя существовали веские исторические причины для выбора стеков, чтобы они росли вниз (и некоторые из них сохраняются, если вы программируете на языке ассемблера и не удосуживаетесь установить правильный кадр стека), они стали менее заметными.
источник