Mengapa kita masih menumbuhkan tumpukan itu?

46

Saat mengkompilasi kode C dan melihat perakitan, semuanya memiliki tumpukan yang tumbuh mundur seperti ini:

_main:
    pushq   %rbp
    movl    $5, -4(%rbp)
     popq    %rbp
    ret

-4(%rbp)- apakah ini berarti pointer dasar atau stack pointer sebenarnya bergerak ke bawah alamat memori daripada naik? Mengapa demikian?

Aku berubah $5, -4(%rbp)untuk $5, +4(%rbp), disusun dan berlari kode dan tidak ada kesalahan. Jadi mengapa kita harus tetap mundur di tumpukan memori?

alex
sumber
2
Catatan yang sama -4(%rbp)sekali tidak memindahkan pointer dasar dan itu +4(%rbp)tidak mungkin bekerja.
Margaret Bloom
14
" mengapa kita harus tetap mundur " - menurut Anda apa keuntungan maju ke depan? Pada akhirnya, itu tidak masalah, Anda hanya harus memilih satu.
Bergi
31
"mengapa kita menumbuhkan tumpukan itu?" - karena jika kita tidak ada orang lain yang akan bertanya mengapa mallocmenumbuhkan tumpukan
slebetman
2
@MargaretBloom: Rupanya pada platform OP, kode startup CRT tidak peduli jika mainmengacaukan RBP-nya. Itu tentu saja mungkin. (Dan ya, tulisan 4(%rbp)akan menginjak nilai RBP yang disimpan). Err sebenarnya, main ini tidak pernah mov %rsp, %rbp, jadi akses memori relatif terhadap RBP pemanggil , jika itu yang benar-benar diuji oleh OP !!! Jika ini benar-benar disalin dari keluaran kompiler, beberapa instruksi ditinggalkan!
Peter Cordes
1
Sepertinya saya bahwa "mundur" atau "maju" (atau "turun" dan "naik") tergantung pada sudut pandang Anda. Jika Anda menggambarkan memori sebagai kolom dengan alamat rendah di atas, maka menumbuhkan tumpukan dengan mengurangi penunjuk tumpukan akan dianalogikan dengan tumpukan fisik.
jamesdlin

Jawaban:

86

Apakah ini berarti pointer basis atau stack pointer sebenarnya bergerak ke bawah alamat memori daripada naik? Mengapa demikian?

Ya, pushinstruksi mengurangi penunjuk tumpukan dan menulis ke tumpukan, sementara popmelakukan sebaliknya, membaca dari tumpukan dan menambah penunjuk tumpukan.

Ini agak historis karena untuk mesin dengan memori terbatas, tumpukan ditempatkan tinggi dan tumbuh ke bawah, sedangkan tumpukan ditempatkan rendah dan tumbuh ke atas. Hanya ada satu celah "memori bebas" - antara tumpukan & tumpukan, dan celah ini dibagi, salah satu dapat tumbuh ke dalam celah sesuai kebutuhan secara individual. Dengan demikian, program ini hanya kehabisan memori ketika tumpukan dan tumpukan bertabrakan tanpa meninggalkan memori bebas. 

Jika tumpukan dan tumpukan keduanya tumbuh ke arah yang sama, maka ada dua celah, dan tumpukan tidak dapat benar-benar tumbuh ke celah tumpukan (sebaliknya juga bermasalah).

Awalnya, prosesor tidak memiliki instruksi penanganan tumpukan khusus. Namun, ketika dukungan tumpukan ditambahkan ke perangkat keras, ia mengambil pola ini tumbuh ke bawah, dan prosesor masih mengikuti pola ini sampai sekarang.

Orang dapat berargumen bahwa pada mesin 64-bit ada ruang alamat yang cukup untuk memungkinkan banyak celah - dan sebagai bukti, banyak celah harus terjadi ketika suatu proses memiliki banyak utas. Meskipun ini bukan motivasi yang cukup untuk mengubah keadaan, karena dengan sistem kesenjangan ganda, arah pertumbuhan bisa dibilang sewenang-wenang, sehingga tradisi / kompatibilitas cocok dengan skala.


Anda harus mengubah petunjuk CPU tumpukan penanganan untuk mengubah arah tumpukan, atau menyerah pada penggunaan didedikasikan mendorong & bermunculan petunjuk (misalnya push, pop, call, ret, orang lain).

Perhatikan bahwa arsitektur set instruksi MIPS tidak memiliki push& khusus pop, sehingga praktis untuk menumbuhkan tumpukan di kedua arah - Anda mungkin masih menginginkan tata letak memori satu celah untuk proses satu utas, tetapi dapat menumbuhkan tumpukan ke atas dan tumpukan ke bawah. Namun, jika Anda melakukan itu, beberapa kode varargs mungkin memerlukan penyesuaian dalam sumber atau lewat parameter di bawah kap.

(Faktanya, karena tidak ada penanganan stack khusus pada MIPS, kami dapat menggunakan kenaikan sebelum atau sesudah atau sebelum atau sesudah penurunan untuk mendorong ke tumpukan selama kami menggunakan pembalikan yang tepat untuk memunculkan tumpukan, dan juga dengan asumsi bahwa sistem operasi menghormati model penggunaan tumpukan yang dipilih. Memang, dalam beberapa sistem tertanam dan beberapa sistem pendidikan, tumpukan MIPS ditanam ke atas.)

Erik Eidt
sumber
32
Ini bukan hanya pushdan poppada kebanyakan arsitektur, tetapi juga jauh lebih penting interupsi-penanganan, call, ret, dan apa pun yang telah dipanggang-dalam interaksi dengan stack.
Deduplicator
3
ARM dapat memiliki keempat rasa stack.
Margaret Bloom
14
Untuk apa nilainya, saya tidak berpikir "arah pertumbuhan sewenang-wenang" dalam arti bahwa pilihan mana pun sama baiknya. Tumbuh memiliki properti yang meluap akhir penyangga clobbers frame stack sebelumnya, termasuk alamat pengirim yang disimpan. Tumbuh memiliki properti yang meluap-luap di ujung penyangga yang hanya menyimpan penyimpanan di yang sama atau lebih baru (jika penyangga tidak ada di yang terbaru, mungkin ada yang lebih baru) menelepon bingkai, dan mungkin bahkan hanya ruang yang tidak digunakan (semua mengasumsikan penjaga halaman setelah tumpukan). Jadi dari perspektif keamanan, tumbuh tampaknya lebih disukai
R ..
6
@R ..: tumbuh dewasa tidak menghilangkan eksploitasi buffer overrun, karena fungsi rentan biasanya bukan fungsi daun: mereka memanggil fungsi lain, menempatkan alamat pengirim di atas buffer. Fungsi Leaf yang mendapatkan pointer dari pemanggil mereka bisa menjadi sangat rentan untuk menimpa alamat pengirim mereka sendiri. mis. Jika suatu fungsi mengalokasikan buffer pada stack dan meneruskannya gets(), atau apakah strcpy()itu tidak mendapatkan inline, maka pengembalian fungsi - fungsi pustaka tersebut akan menggunakan alamat pengirim yang ditimpa. Saat ini dengan tumpukan yang tumbuh ke bawah, saat penelepon mereka kembali.
Peter Cordes
5
@PeterCordes: Memang komentar saya mencatat bahwa level stack frame yang sama atau lebih baru daripada buffer overflow masih berpotensi untuk ditembus, tapi itu jauh lebih sedikit. Dalam kasus di mana fungsi clobbering adalah fungsi daun yang secara langsung dipanggil oleh fungsi yang buffer-nya (misalnya strcpy), pada sebuah lengkungan di mana alamat pengirim disimpan dalam register kecuali perlu tumpah, tidak ada akses ke clobber pengembalian alamat.
R ..
8

Dalam sistem spesifik Anda tumpukan mulai dari alamat memori tinggi dan "tumbuh" ke bawah ke alamat memori rendah. (kasing simetris dari rendah ke tinggi juga ada)

Dan karena Anda berubah dari -4 dan +4 dan itu berlari bukan berarti itu benar. Tata letak memori dari program yang sedang berjalan lebih kompleks dan tergantung pada banyak faktor lain yang dapat berkontribusi pada fakta bahwa Anda tidak langsung terhempas pada program yang sangat sederhana ini.

nadir
sumber
1

Pointer stack menunjuk pada batas antara memori stack yang dialokasikan dan yang tidak dialokasikan. Menumbuhkannya ke bawah berarti menunjuk pada awal struktur pertama dalam ruang stack yang dialokasikan, dengan item yang dialokasikan lainnya mengikuti pada alamat yang lebih besar. Memiliki pointer menunjuk ke awal struktur yang dialokasikan jauh lebih umum daripada sebaliknya.

Sekarang pada banyak sistem saat ini, ada register terpisah untuk stack frame yang bisa agak andal dibatalkan untuk mengetahui rantai panggilan, dengan penyimpanan variabel lokal diselingi. Cara register tumpukan bingkai ini diatur pada beberapa arsitektur berarti ia akhirnya menunjuk di belakang penyimpanan variabel lokal sebagai lawan dari penunjuk tumpukan sebelum itu. Jadi menggunakan register tumpukan bingkai ini kemudian membutuhkan pengindeksan negatif.

Perhatikan bahwa stack frames dan pengindeksannya adalah aspek sisi dari bahasa komputer yang dikompilasi sehingga pembuat kode kompilerlah yang harus berurusan dengan "tidak wajar" daripada programmer bahasa assembly yang buruk.

Jadi, sementara ada alasan historis yang baik untuk memilih tumpukan untuk tumbuh ke bawah (dan beberapa dari mereka dipertahankan jika Anda memprogram dalam bahasa assembly dan tidak repot-repot mengatur bingkai tumpukan yang tepat), mereka menjadi kurang terlihat.


sumber
2
"Sekarang pada banyak sistem akhir-akhir ini, ada register terpisah untuk stack frames" Anda ketinggalan zaman. Format informasi debug yang lebih kaya sebagian besar telah menghilangkan kebutuhan akan frame pointer saat ini.
Peter Green