ما الذي يتم تسجيله للحفظ في اصطلاح الاتصال ARM C؟

StackOverflow https://stackoverflow.com/questions/261419

سؤال

لقد مرت فترة من الوقت منذ آخر مرة قمت فيها بتشفير أداة تجميع الذراع وأنا أشعر بالصدأ قليلاً بشأن التفاصيل.إذا قمت باستدعاء دالة C من الذراع، فلا داعي للقلق إلا بشأن حفظ r0-r3 وlr، أليس كذلك؟

إذا كانت الدالة C تستخدم أي سجلات أخرى، فهل هي مسؤولة عن حفظ تلك الموجودة في المكدس واستعادتها؟بمعنى آخر، سيقوم المترجم بإنشاء تعليمات برمجية للقيام بذلك لوظائف لغة C.

على سبيل المثال، إذا استخدمت r10 في وظيفة المجمّع، فلن أحتاج إلى دفع قيمته إلى المكدس، أو إلى الذاكرة، ثم بثه/استعادته بعد استدعاء C، أليس كذلك؟

هذا خاص بـarm-eabi-gcc 4.3.0.

هل كانت مفيدة؟

المحلول

ان ذلك يعتمد على أبي للنظام الأساسي الذي تقوم بتجميعه.في نظام التشغيل Linux، يوجد نوعان من واجهات ARM ABI؛القديم والجديد.AFAIK، الإصدار الجديد (EABI) هو في الواقع AAPCS الخاص بـ ARM.تعريفات EABI الكاملة موجودة حاليًا هنا في مركز معلومات ARM.

من AAPCS، §5.1.1:

  • ص0-ص3 هي الوسيطة وسجلات الصفر. r0-r1 هي أيضا سجلات النتيجة
  • r4-r8 هي سجلات حفظ المستدعي
  • ص9 قد يكون سجل حفظ المستدعي أم لا (في بعض إصدارات AAPCS يكون سجلًا خاصًا)
  • r10-r11 هي سجلات حفظ المستدعي
  • r12-r15 هي سجلات خاصة

يجب أن يقوم المستدعي بحفظ سجل حفظ المتصل (على عكس سجل حفظ المتصل، حيث يحفظ المتصل السجل)؛لذا، لو هذه هي واجهة برمجة التطبيقات (ABI) التي تستخدمها، وليس عليك حفظ r10 قبل استدعاء وظيفة أخرى (الوظيفة الأخرى مسؤولة عن حفظها).

يحرر: المترجم الذي تستخدمه لا يشكل أي فرق؛يمكن تكوين gcc على وجه الخصوص للعديد من واجهات ABI المختلفة، ويمكن أيضًا تغييرها في سطر الأوامر.إن النظر إلى كود المقدمة/الخاتمة الذي ينشئه ليس مفيدًا، لأنه مصمم خصيصًا لكل وظيفة و يمكن للمترجم استخدام طرق أخرى لحفظ السجل (على سبيل المثال، حفظه في منتصف الوظيفة).

نصائح أخرى

لإضافة المعلومات المفقودة في سجلات NEON:

من AAPCS, ، §5.1.1 السجلات الأساسية:

  • ص0-ص3 هي الوسيطة وسجلات الصفر. r0-r1 هي أيضا سجلات النتيجة
  • r4-r8 هي سجلات حفظ المستدعي
  • ص9 قد يكون سجل حفظ المستدعي أم لا (في بعض إصدارات AAPCS يكون سجلًا خاصًا)
  • r10-r11 هي سجلات حفظ المستدعي
  • r12-r15 هي سجلات خاصة

من AAPCS، §5.1.2.1 اصطلاحات استخدام تسجيل VFP:

  • s16 – s31 (d8 – d15، q4 – q7) يجب الحفاظ عليها
  • s0 – s15 (d0 – d7، q0 – q3) و d16 – d31 (q8 – q15) لا تحتاج إلى الحفاظ عليها

المشاركة الأصلية:
اتفاقية الاتصال من الذراع إلى C من خلال تسجيلات النيون للحفظ

لـ 64 بت ARM، A64 (من معيار استدعاء الإجراء لبنية ARM 64 بت)

هناك واحد وثلاثون سجلاً للأغراض العامة (عدد صحيح) 64 بت مرئية لمجموعة تعليمات A64؛تم تصنيف هذه r0-r30.في سياق 64 بت، يُشار عادةً إلى هذه السجلات باستخدام الأسماء x0-x30;في سياق 32 بت يتم تحديد السجلات باستخدام w0-w30.بالإضافة إلى ذلك، سجل مؤشر المكدس، SP, ، يمكن استخدامه مع عدد محدود من التعليمات.

  • SP مؤشر المكدس
  • ص30 LR رابط التسجيل
  • ص29 FP مؤشر الإطار
  • ص19…ص28 السجلات المحفوظة كالي
  • ص18 سجل المنصة، إذا لزم الأمر؛وإلا سجل مؤقت.
  • ص17 IP1 السجل المؤقت الثاني للمكالمات المؤقتة (يمكن استخدامه بواسطة قشرة المكالمات ورمز PLT) ؛في أوقات أخرى يمكن استخدامها كسجل مؤقت.
  • ص16 IP0 أول سجل خدش داخل الإجراء (يمكن استخدامه بواسطة قشرة المكالمات ورمز PLT) ؛في أوقات أخرى يمكن استخدامها كسجل مؤقت.
  • ص9…ص15 السجلات المؤقتة
  • ص8 سجل موقع النتيجة غير المباشرة
  • ص0…ص7 سجلات المعلمة/النتيجة

أول ثمانية سجلات، r0-r7, ، تُستخدم لتمرير قيم الوسيطات إلى روتين فرعي ولإرجاع قيم النتائج من دالة.ويمكن أيضًا استخدامها للاحتفاظ بالقيم المتوسطة ضمن الروتين (ولكن، بشكل عام، فقط بين استدعاءات الروتين الفرعي).

السجلات r16 (IP0) و r17 (IP1) يمكن استخدامه بواسطة رابط كسجل مؤقت بين الروتين وأي روتين فرعي يستدعيه.يمكن أيضًا استخدامها ضمن روتين للاحتفاظ بالقيم المتوسطة بين مكالمات الروتين الفرعي.

دور التسجيل ص18 منصة محددة.إذا كانت منصة ABI بحاجة إلى سجل مخصص للأغراض العامة لحمل الحالة بين الإجرائية (على سبيل المثال، سياق مؤشر الترابط) فيجب عليها استخدام هذا السجل لهذا الغرض.إذا لم يكن لدى منصة ABI مثل هذه المتطلبات، فيجب عليها استخدام r18 كسجل مؤقت إضافي.يجب أن توثق مواصفات ABI للنظام الأساسي استخدام هذا السجل.

SIMD

تحتوي بنية ARM 64 بت أيضًا على اثنين وثلاثين سجلًا إضافيًا، v0-v31, والتي يمكن استخدامها من خلال عمليات SIMD والنقطة العائمة.سيتم تغيير الاسم الدقيق للسجل للإشارة إلى حجم الوصول.

ملحوظة: على عكس AArch32، في AArch64، لا تتداخل طرق العرض 128 بت و64 بت لسجل SIMD والنقطة العائمة مع تسجيلات متعددة في عرض أضيق. لذلك يشير كل من q1 وd1 وs1 إلى نفس الإدخال في بنك التسجيل.

أول ثمانية سجلات، v0-v7, ، تُستخدم لتمرير قيم الوسيطات إلى روتين فرعي ولإرجاع قيم النتائج من دالة.ويمكن أيضًا استخدامها للاحتفاظ بالقيم المتوسطة ضمن الروتين (ولكن، بشكل عام، فقط بين استدعاءات الروتين الفرعي).

السجلات v8-v15 يجب أن يتم الاحتفاظ بها بواسطة المستدعى عبر المكالمات الروتينية؛السجلات المتبقية (الإصدار 0-الإصدار 7، الإصدار 16-الإصدار 31) لا تحتاج إلى الحفاظ عليها (أو يجب أن يحفظها المتصل).بالإضافة إلى ذلك، يتم تخزين الجزء السفلي 64 بت فقط من كل قيمة v8-v15 تحتاج إلى الحفاظ عليها.وتقع على عاتق المتصل مسؤولية الحفاظ على القيم الأكبر.

قدمت إجابات CesarB وPavel اقتباسات من AAPCS، ولكن لا تزال هناك قضايا مفتوحة.هل يقوم المستدعي بحفظ r9؟ماذا عن r12؟ماذا عن r14؟علاوة على ذلك، كانت الإجابات عامة جدًا، وليست خاصة بسلسلة أدوات Arm-eabi كما هو مطلوب.فيما يلي طريقة عملية لمعرفة أي السجلات تم حفظها للمتصلين وأيها ليست كذلك.

يحتوي كود C التالي على كتلة تجميع مضمّنة، تدعي تعديل السجلات r0-r12 وr14.سيقوم المترجم بإنشاء التعليمات البرمجية لحفظ السجلات المطلوبة بواسطة ABI.

void foo() {
  asm volatile ( "nop" : : : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r14");
}

استخدم سطر الأوامر arm-eabi-gcc-4.7 -O2 -S -o - foo.cوأضف المفاتيح الخاصة بالنظام الأساسي الخاص بك (مثل -mcpu=arm7tdmi على سبيل المثال).سيقوم الأمر بطباعة رمز التجميع الذي تم إنشاؤه على STDOUT.قد يبدو الأمر كالتالي:

foo:
    stmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    nop
    ldmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    bx  lr

لاحظ أن الكود الذي أنشأه المترجم يحفظ ويستعيد r4-r11.لا يقوم المترجم بحفظ r0-r3، r12.إن استعادة r14 (الاسم المستعار lr) هو أمر عرضي تمامًا لأنني أعلم من التجربة أن رمز الخروج قد يقوم أيضًا بتحميل lr المحفوظ في r0 ثم القيام بـ "bx r0" بدلاً من "bx lr".إما بإضافة -mcpu=arm7tdmi -mno-thumb-interwork أو باستخدام -mcpu=cortex-m4 -mthumb نحصل على رمز تجميع مختلف قليلاً يشبه هذا:

foo:
    stmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, lr}
    nop
    ldmfd   sp!, {r4, r5, r6, r7, r8, r9, sl, fp, pc}

مرة أخرى، يتم حفظ r4-r11 واستعادته.لكن لم تتم استعادة r14 (الاسم المستعار lr).

كي تختصر:

  • r0-r3 هي لا المستدعي المحفوظ
  • تم حفظ r4-r11
  • r12 (الاسم المستعار ip) هو لا المستدعي المحفوظ
  • r13 (الاسم المستعار sp) محفوظ في المستدعي
  • r14 (الاسم المستعار lr) هو لا المستدعي المحفوظ
  • r15 (الاسم المستعار pc) هو عداد البرنامج ويتم ضبطه على قيمة lr قبل استدعاء الوظيفة

ينطبق هذا على الأقل على الإعدادات الافتراضية لـarm-eabi-gcc.هناك مفاتيح تبديل سطر الأوامر (على وجه الخصوص رمز التبديل -mabi) التي قد تؤثر على النتائج.

يوجد أيضًا اختلاف على الأقل في بنية Cortex M3 لاستدعاء الوظيفة والمقاطعة.

في حالة حدوث مقاطعة، سيتم إجراء دفع تلقائي لـ R0-R3، R12، LR، PC إلى Stack وعند إرجاع نموذج IRQ POP التلقائي.إذا كنت تستخدم سجلات أخرى في روتين IRQ، فيجب عليك دفعها/وضعها على Stack يدويًا.

لا أعتقد أن هذا PUSH وPOP التلقائي تم إنشاؤه لاستدعاء وظيفة (تعليمات القفز).إذا كانت الاتفاقية تنص على أنه يمكن استخدام R0-R3 فقط كوسيطة أو نتيجة أو تسجيلات مؤقتة، لذلك ليست هناك حاجة لتخزينها قبل استدعاء الوظيفة لأنه لا ينبغي أن تكون هناك أي قيمة تستخدم لاحقًا بعد إرجاع الوظيفة.ولكن كما هو الحال في المقاطعة، يجب عليك تخزين جميع سجلات وحدة المعالجة المركزية الأخرى إذا كنت تستخدمها في وظيفتك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top