Program structure in assembly

מה קורה כשאנחנו כותבים int n= 10 בתוכנית שלנו? הקומפיילר מקצה למשתנה הזה כתובת בזכרון ומגדיר לו תג n .
באסמבלי, אנחנו נעשה את הדברים האלה בעצמו, כדי להבין בכלל איך עושים את זה, נבין שהתוכנית שלנו מחולקת ל 4 חלקים עיקריים

Pasted image 20221223165536.png|300

data - segment

בתוכנית אסמבלי הסגמנט הזה יסומן על ידי data.
לכל דבר שנרצה שיהיה לו זכרון בdata נשייך label כשלהו בסינתקס :my_label , ניתן לשים נקודה לפני label או לתת לו ערך שהוא ספרה בין 0 ל 9 ואז הוא numeric אבל לא אכנס לזה בסיכום הזה.

counter: .word 15

במערכת little endian זה ייראה ככה בזכרון

00001111counter 0000000counter+1

נוכל לתת מספר ערכים לאלמנט ב data למשל

.data
	alice: .byte       3,5,10,-7

בזכרון זה יישמר ככה

Pasted image 20221223173337.png|340
כמו כן נשים לב שאם היינו שמים אלמנט נוסף מתחת לאליס אז הגישה אליו בזכרון הייתה רציפה כלומר alice+4 היה מוביל אותנו לאלאמנט הזה.

איך לגשת למשתנים האלו?
נניח שיש לנו את הlabels הבאים

a: .quad 1000
b: .word 0x10
c: .byte 3

נגשים לכל אחד מהם כאילו ניגשים ל immediate, למשל

movq $b , %rax

הסימון הזה מחזיר את ה״ערך המיידי של a" כלומר הכתובת בזכרון של a. זה קצת מבלבל כי אם היינו עושים את הסימון הזה על מספר היינו מקבלים את הערך עצמו ולא את הכתובת, אבל ה immediate של לייבל זה הכתובת בזכרון.

section rodata

החלק הזה section .rodata. הוא מכיל רק read only data. למשל סטרינגים קבועים וכו. למשל

.section .rodata
format1:  .string "%d +0x%x =%d\n"
format2:  .string "%ld*2%d = %ld\n"

text segment

יתחיל ב text. ויכיל את החלק של הקוד עצמו. למשל

.text
addl $20, %eax

גם לפעולות יש כתובת ונגע בזה בהמשך בפונקציות.

the stack segment

אנחנו יכולים לקרוא ולכתוב לאן שאנחנו רוצים במחסנית כל עוד לא חרגנו ממנה באמצעו ה rsp . שתמיד מצביע לראש המחסנית בעת שלב כלשהו בתוכנית.
לכל פונקצייה שתכנס למחסנית נוסיף אליה זכרון במבנה של LIFO.

נשים לב שהמחסנית היא מימוש שלנו למבנה המחסנית המוכר שאנחנו מכירים מתוכניות c. כלומר ככל הנראה יהיו אי דיוקים ביחס למה שאנחנו עושים לאיך שהקומפיילר מממש את זה במדויק.
כמו כן יש גם תהליכים אוטומטיים שגורמים לתזוזה של המחסנית בזכרון כמו call לפונקצייה ו ret .
כמו כן נשים לב שהמחסנית והערימה ״חולקות״ את אותו מרחב זכרון והם מתקרבים אחד לשני, כלומר, על ידי החסרה של rsp אנחנו מוסיפים לראש המחסנית.
בסיום תהליך כלשהו שקשור למחסנית, נעלה את כתובת המחסנית בחזרה.

rbp

אמרנו ש rsp מצביע לראש המחסנית, rbp הוא רגיסטר שמשמש כframe pointer. כלומר כאשר אנחנו לא יודעים בכמה הstack frame גדל, נרצה להשתמש בrbp כדי להחזיק מצביע לראש המחסנית הקודם. (ניתן להשתמש בו כסתם register אם אנחנו יודעים בכמה המחסנית גדלה וקטנה).

אנחנו יודעים שה rsp תמיד מצביע לסוף הזכרון הדרוש עבור פעולה כלשהי ולאט לאט מתחיל לחזור אחורה (במקרה הזה להעלות בערכי הכתובות) . ה rbp מאפשר לנו להחזיק את הכתובת של תחילת התהליך ולא של סופו ובאופן הזה נוכל להתחיל מהתחלה אם קרתה איזשהי שגיאה בניהול ה sp.

Pasted image 20221223214820.png|300
נשים לב שהשימוש ב rbp רלוונטי כאשר אנחנו לא יודעים מהו הגודל שנצטרך להוסיף ל rsp , אם זה גודל קבוע אז לא צריך אותו וניתן להשתמש בו כregister כללי.

נוכל להשתמש בכתובות המחסנית כדי לבצע השמה לערכים מרגיסטרים באופן הבא:

	subq $8, %rsp
	movq %rdi, (%rsp)

או שנשתמש בפקודה pushq %rdi , הפעולה הזאת תזיז באופן אוטומטי את rsp בקפיצה של 8 בייטים.
באופן דומה נשתמש ב popq כדי להוריד איברים מהמחסנית ולהגדיל את rsp.

בתחילת תוכנית לרוב נראה את שתי הפקודות הבאות

main:
    # in case of a variable-size stack frame:
	pushq  %rbp  #saving the old frame pointer
	movq   %rsp, %rbp #creating the new frame pointer.