ال

الأساسيات في x86-64 Assembly

1 تمرين

نبذة عن الأساسيات

التعليقات

يستخدم مسار x86-64 في Exercism المُجمِّع NASM (The Netwide Assembler). في NASM، تُسبق التعليقات بفاصلة منقوطة (;).

يمكن وضع التعليقات في أي مكان في البرنامج، وكل ما يأتي بعد ; يتجاهله المُجمِّع.

الثوابت

يمكن تعريف ثابت في وقت التجميع في NASM باستخدام equ. على سبيل المثال، يُعرّف هذا ثابتًا باسم UNIVERSE وقيمته 42:

UNIVERSE equ 42

تُقيَّم الثوابت مرة واحدة عند تعريفها، ولا يمكن إعادة تعريفها لاحقًا.

السجلات ذات الأغراض العامة (GPRs)

في لغة التجميع، نستخدم عادةً السجلات لتخزين القيم بدلًا من المتغيرات. سجل المعالج هو قطعة من ذاكرة سريعة داخل معالج الحاسوب. وتُنفَّذ معظم العمليات الحسابية في سجلات المعالج.

تُستخدم بعض السجلات في مجموعة متنوعة من العمليات المختلفة، وتُسمّى السجلات ذات الأغراض العامة (GPRs). أما البعض الآخر فله أغراض خاصة أو مخصّصة.

في x86-64، هناك 16 سجلًا من السجلات ذات الأغراض العامة بعرض 64 بت، ويمكن الوصول إليها أيضًا بعرض 32 بت أو 16 بت أو 8 بت. توضّح القائمة التالية هذه السجلات، حيث تتراوح قيمة n في rn من 8 إلى 15: r8 وr9 وr10 وr11 وr12 وr13 وr14 وr15.

64 بت 32 بت 16 بت 8 بت
rax eax ax ah/al
rbx ebx bx bh/bl
rcx ecx cx ch/cl
rdx edx dx dh/dl
rsi esi si sil
rdi edi di dil
rbp ebp bp bpl
rsp esp sp spl
rn rnd rnw rnb

عند استخدام أقل من 64 بت، تكون البتات التي يتم الوصول إليها عادةً من الجزء السفلي من السجل. ويُشكّل ah وbh وch وdh استثناءً من هذه القاعدة، فهي تصل إلى البتات الثماني العليا من الجزء المكوّن من 16 بت في السجل.

توضيح لكيفية الوصول إلى البتات في السجل rax:

+--------+---------------------------------------+
| 64-bit |                  rax                  |
+--------+-------------------+-------------------+
| 32-bit |                   |        eax        |
+--------+-------------------+---------+---------+
| 16-bit |                             |    ax   |
+--------+-----------------------------+----+----+
| 8-bit  |                             | ah | al |
+--------+-----------------------------+----+----+

يجب الحفاظ على بعض هذه السجلات عبر استدعاءات الدوال: rbp وrsp وrbx وr12 وr13 وr14 وr15. وقد يؤدي عدم الحفاظ عليها إلى خطأ أو إلى سلوك غير معرّف.

أما السجلات الأخرى فلا يُحافظ عليها ويمكن استخدامها بحرية: rax وrcx وrdx وrdi وrsi وr8 وr9 وr10 وr11.

Note

تُحفظ السجلات عادةً على المكدّس باستخدام التعليمة push. ثم تُستعاد قبل نهاية الدالة باستخدام التعليمة pop.

التعليمات

التعليمات هي أجزاء من العمليات الحسابية التي يمكن للمعالج تنفيذها. وتأخذ عادةً الشكل التالي:

name destination, source

فاسم التعليمة يأتي أولًا، ثم مسافة واحدة على الأقل، تليها المعامل الوجهة، ثم فاصلة (,) وأخيرًا المعامل المصدر. ولا تُعدّل التعليمة عادةً المعامل المصدر، بل المعامل الوجهة فقط.

ويجب أن يكون المعاملان بالحجم نفسه ما لم يُذكر خلاف ذلك. على سبيل المثال، يجب أن يكون المعامل الوجهة بعرض 16 بت أيضًا إذا كان المعامل المصدر بعرض 16 بت.

على سبيل المثال، لتخزين قيمة في سجل، يمكننا استخدام التعليمة mov:

mov rax, rdx  ; rax = rdx
              ; mov is the name, rax is the destination operand and rdx is the source operand

ينسخ المقطع أعلاه محتوى البتات الـ64 كاملة في rdx، وهو المعامل المصدر، إلى البتات الـ64 كاملة في rax، وهو المعامل الوجهة للتعليمة.

ويمكن استخدام أعداد صحيحة ثابتة كمعاملات مصدر (وليس كمعاملات وجهة) في معظم التعليمات:

mov rcx, 42 ; rcx = 42
            ; rcx is the destination operand and 42 is the source operand

لاحظ أن الكتابة في سجل بعرض 32 بت تمحو أيضًا البتات العليا، لذا فإن mov eax, 42 تكافئ mov rax, 42. وهذا لا ينطبق على السجلات بعرض 16 بت و8 بت.

بالنسبة للعمليات الحسابية الجمع والطرح والضرب، يمكننا استخدام التعليمات add وsub وimul:

add rax, rsi ; rax = rax + rsi
imul rax, rdi ; rax = rax * rdi
sub rax, r8 ; rax = rax - r8

الدوال

تُنظَّم التعليمات في دوال. وتوضع جميع الدوال في section .text من ملف المصدر.

ويتكوّن تعريف الدالة من:

  1. تسمية تحمل اسم الدالة، تليها :.
  2. التعليمات التي تعرّف الدالة.
  3. تعليمة الإرجاع، ret.

وهناك اصطلاحات محددة لكيفية تمرير الوسائط وإرجاعها من الدالة. والاصطلاحان الرئيسيان للاستدعاء هما System V AMD64 ABI وMicrosoft x64.

يستخدم هذا المسار اصطلاح الاستدعاء System V AMD64 ABI، وتُمرَّر الوسائط الصحيحة الستة الأولى إلى الدالة عبر السجلات. وتُمرَّر بالترتيب التالي: rdi وrsi وrdx وrcx وr8 وr9.

وتُرجَع القيمة الصحيحة من الدالة في السجل rax.

لاستدعاء دالة، نستخدم التعليمة call. ويمكن استدعاء الدالة من أي مكان في ملف المصدر نفسه. ولجعل الدالة مرئية لملفات المصدر الأخرى، يجب استخدام التوجيه global.

على سبيل المثال، يعرّف هذا دالة sum:

section .text ; functions are placed here

global sum ; sum is visible to other source files

sum:
    ; first argument is passed in rdi
    ; second argument is passed in rsi
    ; return value is placed in rax

    mov rax, rdi ; rax is now equal to rdi
    add rax, rsi ; rax = rax + rsi

    ret ; function return

ويستدعي هذا دالة sum لدينا بالوسيطين 3 و5:

mov rdi, 3  ; First argument in rdi
mov rsi, 5  ; Second argument in rsi
call sum

; The rax register now contains the value 8 (3 + 5), after sum returns
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة

تعلّم الأساسيات