تستخدم أجهزة Android المختلفة وحدات معالجة مركزية مختلفة، والتي تتوافق بدورها مع مجموعات تعليمات مختلفة. لكل مجموعة من وحدة المعالجة المركزية ومجموعة التعليمات واجهة برمجة تطبيقات ثنائية خاصة بها (ABI). تتضمّن ABI المعلومات التالية:
- هي مجموعة تعليمات وحدة المعالجة المركزية (CPU) (والإضافات) التي يمكن استخدامها.
- يتم تخزين تنسيق endian للذاكرة وتحميله أثناء التشغيل. يتميز Android دائمًا بالبيانات المحدودة.
- اصطلاحات لنقل البيانات بين التطبيقات والنظام، بما في ذلك قيود المحاذاة، وكيفية استخدام النظام للرصِد والملف الشخصي عند استدعاء الدوالّ
- يشير ذلك المصطلح إلى تنسيق الملفات الثنائية القابلة للتنفيذ، مثل البرامج والمكتبات المشتركة، وأنواع المحتوى التي تدعمها. يستخدم Android دائمًا تنسيق ELF. لمزيد من المعلومات، يُرجى الاطّلاع على ELF System V Application Binary Interface.
- كيفية تشويه أسماء C++ لمزيد من المعلومات، يُرجى الاطّلاع على Generic/Itanium C++ ABI.
تقدّم هذه الصفحة قائمة بواجهات برمجة التطبيقات (ABI) التي يتوافق معها حِزم NDK، كما تقدّم معلومات حول آلية عمل كل واجهة برمجة تطبيقات.
يمكن أن تشير واجهة التطبيق الثنائية (ABI) أيضًا إلى واجهة برمجة التطبيقات الأصلية التي تتوافق معها المنصة. للاطّلاع على قائمة بأنواع مشاكل ABI التي تؤثر في الأنظمة التي تعمل بتقنية 32 بت، يُرجى الاطّلاع على الأخطاء في ABI على الأنظمة التي تعمل بتقنية 32 بت.
واجهات ABI المتوافقة
ABI | مجموعات التعليمات المتوافقة | ملاحظات |
---|---|---|
armeabi-v7a |
|
غير متوافق مع أجهزة ARMv5/v6. |
arm64-v8a |
فقط مع Armv8.0 | |
x86 |
لا يتوافق أي من ملفات MOVBE أو SSE4. | |
x86_64 |
|
x86-64-v1 بالكامل، وx86-64-v2 جزئيًا فقط (بدون LAHF-SAHF) |
ملاحظة: في السابق، كان NDK متوافقًا مع ARMv5 (armeabi) وMIPS بإصدارَي 32 و64 بت، ولكن تمّ إيقاف توافقه مع واجهات برمجة التطبيقات هذه في الإصدار 17 من NDK.
armeabi-v7a
هذا المعرّف المتوافق مع واجهة برمجة التطبيقات مخصّص لوحدات المعالجة المركزية ARM ذات الـ 32 بت. ويشمل ذلك Thumb-2 وNeon.
للحصول على معلومات عن أجزاء ABI غير المخصّصة لنظام التشغيل Android، يُرجى الاطّلاع على واجهة التطبيق الثنائية (ABI) لبنية ARM.
تُنشئ أنظمة إنشاء NDK رمز Thumb-2 تلقائيًا ما لم تستخدم
LOCAL_ARM_MODE
في Android.mk
لملف برمجي ملف برمجي ANDROID_ARM_MODE
عند ضبط CMake.
للحصول على مزيد من المعلومات حول تاريخ Neon، يمكنك الاطّلاع على دعم Neon.
لأسباب تاريخية، يستخدم معرّف ABI هذا -mfloat-abi=softfp
، ما يؤدي إلى تمرير جميع قيم float
في سجلات الأعداد الصحيحة وتمرير جميع قيم double
في أزواج سجلات الأعداد الصحيحة عند إجراء طلبات بيانات الدوالّ. على الرغم من الاسم، لا يؤثر هذا الإعداد إلا في اصطلاح استدعاء النقطة العائمة: سيظل المُجمِّع يستخدم تعليمات النقطة العائمة للأجهزة في العمليات الحسابية.
تستخدم واجهة ABI هذه long double
64 بت (IEEE ثنائية64 مثل double
).
arm64-v8a
هذا المعرّف المتوافق مع واجهة برمجة التطبيقات مخصّص لوحدات المعالجة المركزية ARM 64 بت.
اطّلِع على التعرّف على البنية من Arm للحصول على تفاصيل كاملة عن أجزاء ABI غير الخاصة بنظام التشغيل Android. تقدّم Arm أيضًا بعض النصائح حول النقل في تطوير تطبيقات Android بنظام 64 بت.
يمكنك استخدام وظائف Neon الأساسية في رمز C وC++ للاستفادة من إضافة Advanced SIMD. يقدّم دليل مبرمِجين النيون لـ Armv8-A مزيدًا من المعلومات حول سمات النيون وبرمجة النيون بشكل عام.
على نظام التشغيل Android، يتم تخصيص سجلّ x18 الخاص بالنظام الأساسي لـ
ShadowCallStack
ويجب ألا يتأثر بالرمز البرمجي. تستخدم الإصدارات الحالية من Clang تلقائيًا
الخيار -ffixed-x18
على Android، لذا لا داعي للقلق بشأن ذلك ما لم يكن لديك مجمع تعليمات مكتوبة يدويًا (أو مترجم قديم جدًا).
تستخدِم واجهة ABI هذه long double
بسعة 128 بت (IEEE binary128).
x86
هذا المعرّف المتوافق مع معيار ABI مخصّص لوحدات المعالجة المركزية التي تتوافق مع مجموعة التعليمات المعروفة باسم "x86" أو "i386" أو "IA-32".
تتضمّن واجهة برمجة التطبيقات (ABI) في Android مجموعة التعليمات الأساسية بالإضافة إلى MMX وSSE وSSE2 وSSE3 وSSSE3.
لا يتضمّن واجهة التطبيق الثنائية (ABI) أي إضافات اختيارية أخرى لمجموعة تعليمات IA-32، مثل MOVBE أو أي صيغة من SSE4. لا يزال بإمكانك استخدام هذه الإضافات ما دمت تستخدم التحقق من الميزات في وقت التشغيل لتفعيلها، وتوفّر إجراءات احتياطية للأجهزة غير المتوافقة.
تفترض سلسلة أدوات NDK محاذاة الحزمة بحجم 16 بايت قبل استدعاء دالة. تفرض الأدوات وخيارات الإعداد التلقائية هذه القاعدة. إذا كنت تكتب رمزًا لنصّ التجميع، عليك التأكّد من الحفاظ على محاذاة الرمز البرمجي للتكديس، والتأكّد من أنّ المجمعات الأخرى تلتزم أيضًا بهذه القاعدة.
يُرجى الرجوع إلى المستندات التالية للحصول على مزيد من التفاصيل:
- اتّفاقيات الاتّصال لمختلف أنظمة التشغيل وبرامج التحويل البرمجي لـ C++
- Intel IA-32 Intel Architecture Software Developer's Manual, Volume 2: Instruction Set Reference
- Intel IA-32 Intel Architecture Software Developer's Manual, Volume 3: System Programming Guide
- System V Application Binary Interface: Intel386 Processor Architecture Supplement
يستخدم معرّف ABI هذا long double
بسعة 64 بت (IEEE binary64 مثل double
، وليس long double
بسعة 80 بت المشترَك بشكلٍ أكبر والذي يستخدمه Intel فقط).
x86_64
هذا المعرّف المتوافق مع معيار ABI مخصّص لوحدات المعالجة المركزية التي تتوافق مع مجموعة التعليمات التي يُشار إليها عادةً باسم "x86-64".
تتضمن واجهة التطبيق الثنائية (ABI) في Android مجموعة التعليمات الأساسية بالإضافة إلى MMX وSSE وSSE2 وSSE3 وSSSE3 وSSE4.1 وSSE4.2 وتعليمات POPCNT.
لا يتضمّن معيار ABI أيّ مجموعات تعليمات أو تكميلات اختيارية أخرى لبنية x86-64، مثل MOVBE أو SHA أو أيّ نوع من AVX. سيظل بإمكانك استخدام هذه الإضافات، شرط استخدام فحص ميزات وقت التشغيل لتفعيلها، وتوفير بدائل للأجهزة التي لا تتوافق معها.
يُرجى الرجوع إلى المستندات التالية للحصول على مزيد من التفاصيل:
- اصطلاحات حول برامج التحويل البرمجي وأنظمة التشغيل المختلفة لـ C++
- Intel64 and IA-32 Architectures Software Developer's Manual, Volume 2: Instruction Set Reference
- Intel64 and IA-32 Intel Architecture Software Developer's Manual Volume 3: System Programming
تستخدِم واجهة ABI هذه long double
بسعة 128 بت (IEEE binary128).
إنشاء رمز لواجهة التطبيق الثنائية (ABI) المحدّدة
Gradle
ينشئ Gradle (سواءً كان ذلك من خلال "استوديو Android" أو من سطر الأوامر) التطبيقات ليعملوا على
جميع واجهات برمجة التطبيقات غير المتوقّفة نهائيًا تلقائيًا. لتقييد مجموعة واجهات التطبيق الثنائية (ABI)
التي يتوافق معها تطبيقك، استخدِم abiFilters
. على سبيل المثال، لإنشاء إصدار متوافق مع واجهات ABI فقط
بسعة 64 بت، اضبط الإعدادات التالية في build.gradle
:
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'x86_64'
}
}
}
ndk-build
ينشئ ndk-build جميع واجهات ABI غير المتوقّفة نهائيًا تلقائيًا. يمكنك استهداف IDE معيّنة من خلال ضبط APP_ABI
في ملف Application.mk. يعرض المقتطف التالي بعض الأمثلة على استخدام APP_ABI
:
APP_ABI := arm64-v8a # Target only arm64-v8a
APP_ABI := all # Target all ABIs, including those that are deprecated.
APP_ABI := armeabi-v7a x86_64 # Target only armeabi-v7a and x86_64.
لمزيد من المعلومات عن القيم التي يمكنك تحديدها لـ APP_ABI
، راجِع Application.mk.
CMake
باستخدام CMake، أنت تنشئ لواجهة ABI واحدة في كل مرة وعليك تحديد واجهة التطبيق الثنائية (ABI)
بشكل صريح. ويمكنك إجراء ذلك باستخدام المتغيّر ANDROID_ABI
الذي يجب تحديده في سطر الأوامر (لا يمكن ضبطه في CMakeLists.txt). على سبيل المثال:
$ cmake -DANDROID_ABI=arm64-v8a ...
$ cmake -DANDROID_ABI=armeabi-v7a ...
$ cmake -DANDROID_ABI=x86 ...
$ cmake -DANDROID_ABI=x86_64 ...
لمعرفة العلامات الأخرى التي يجب تمريرها إلى CMake لبناء المشروع باستخدام حزمة NDK، يُرجى الاطّلاع على دليل CMake.
السلوك التلقائي لنظام الإنشاء هو تضمين الملفات الثنائية لكل بنية أساسية لنظام التشغيل (ABI) في حزمة APK واحدة، والتي تُعرف أيضًا باسم حزمة APK كبيرة. ملف APK الموسّع حجمه أكبر بكثير مقارنةً بملف APK الذي يحتوي على الثنائيات لبنية ABI واحدة فقط، ويكون خيار استخدام ملف APK الموسّع مفيدًا في حال أردت تحسين التوافق مع الأجهزة المختلفة، ولكن على حساب حجم ملف APK الأكبر. وننصح بشدة بالاستفادة من حِزم التطبيقات أو تقسيمات حِزم APK لتقليل حجم حِزم APK مع الحفاظ على أقصى قدر من التوافق مع الأجهزة.
في وقت التثبيت، لا يفكّ مدير الحِزم سوى رمز الآلة الأنسب للجهاز المستهدَف. لمعرفة التفاصيل، يُرجى الاطّلاع على الاستخراج التلقائي ل الرمز البرمجي الأصلي في وقت التثبيت.
إدارة ABI على نظام Android الأساسي
يوفّر هذا القسم تفاصيل حول كيفية إدارة نظام Android للرمز البرمجي المضمّن في حِزم APK.
الرموز البرمجية الأصلية في حِزم التطبيقات
يتوقّع كلّ من "متجر Play" و"مدير الحِزم" العثور على مكتبات من إنشاء حِزم تطوير البرامج (NDK) في مسارات الملفات داخل حزمة APK تطابق النمط التالي:
/lib/<abi>/lib<name>.so
في هذه الحالة، <abi>
هو أحد أسماء ABI المدرَجة ضمن ABIs المتوافقة،
و<name>
هو اسم المكتبة كما حدّدته للمتغيّر LOCAL_MODULE
في ملف Android.mk
. بما أنّملفات APK ليست سوى ملفات zip، من السهل فتحها والتأكّد من أنّ مكتبات برمجية محلية مشتركة
في المكان الصحيح.
إذا لم يعثر النظام على المكتبات المشتركة الأصلية في المكان المتوقّع، لن يتمكّن من استخدامها. في هذه الحالة، على التطبيق نفسه نسخ المكتبات ثم
تنفيذ dlopen()
.
في ملف APK fat، تتوفّر كل مكتبة ضمن دليل يتطابق اسمه مع واجهة ABI مقابل. على سبيل المثال، قد يحتوي ملف APK غني بالدهون على ما يلي:
/lib/armeabi/libfoo.so /lib/armeabi-v7a/libfoo.so /lib/arm64-v8a/libfoo.so /lib/x86/libfoo.so /lib/x86_64/libfoo.so
ملاحظة: في أجهزة Android المستندة إلى ARMv7 التي تعمل بالإصدار 4.0.3 أو إصدار أقدم، تتم
تثبيت المكتبات الأصلية من الدليل armeabi
بدلاً من الدليل armeabi-v7a
إذا كان كلا الدليلَين متوفّرَين. ويرجع ذلك إلى أنّ /lib/armeabi/
يأتي بعد
/lib/armeabi-v7a/
في حزمة APK. تم حل هذه المشكلة من الإصدار 4.0.4.
توافق واجهات ABI لنظام Android الأساسي
يتعرّف نظام Android على واجهات ABI التي يدعمها أثناء التشغيل، لأنّ سمات النظام المخصّصة للإصدار تشير إلى ما يلي:
- تمثّل هذه السمة واجهة ABI الأساسية في الجهاز، وتتوافق مع رمز الجهاز المستخدَم في صورة النظام نفسه.
- واجهات ABI الثانوية الاختيارية، والتي تتوافق مع واجهات ABI الأخرى التي تتوافق معها أيضًا صورة النظام
تضمن هذه الآلية أن يستخرج النظام أفضل رمز آلي من الحزمة في وقت التثبيت.
للحصول على أفضل أداء، يجب إجراء عملية الترجمة مباشرةً لملف ABI الأساسي. على سبيل المثال، لن يحدِّد
الجهاز النموذجي المستنِد إلى ARMv5TE سوى ABI الأساسي: armeabi
. في المقابل، سيحدِّد
الجهاز النموذجي المستنِد إلى ARMv7 معرّف ABI الأساسي على أنّه armeabi-v7a
والمعرّف الثانوي
على أنّه armeabi
، لأنّه يمكنه تشغيل ملفات التطبيق الثنائية الأصلية التي تم إنشاؤها لكل منهما.
تتيح الأجهزة التي تعمل بنظام 64 بت أيضًا استخدام الإصدارات المتوافقة مع 32 بت. على سبيل المثال، باستخدام أجهزة arm64-v8a، يمكن للجهاز أيضًا تشغيل رمز armeabi وarmeabi-v7a. تجدر الإشارة إلى أنّه سيكون أداء تطبيقك أفضل بكثير على الأجهزة التي تعمل بإصدار 64 بت إذا كان يستهدف arm64-v8a بدلاً من الاعتماد على الجهاز الذي يعمل بإصدار armeabi-v7a من تطبيقك.
يمكن أيضًا للعديد من الأجهزة المستندة إلى x86 تشغيل ملفَي armeabi-v7a
وarmeabi
NDK الثنائيَين. بالنسبة إلى هذه الأجهزة، سيكون واجهة التطبيق الثنائية (ABI) الأساسية هي x86
وواجهة التطبيق الثانية armeabi-v7a
.
يمكنك فرض تثبيت حزمة APK لـ ABI معيّن. يكون ذلك مفيدًا للاختبار. استخدِم الأمر التالي:
adb install --abi abi-identifier path_to_apk
الاستخراج التلقائي للرمز البرمجي الأصلي في وقت التثبيت
عند تثبيت تطبيق، تفحص خدمة مدير الحِزم حزمة APK وتبحث عن أي مكتبات مشترَكة من الشكل التالي:
lib/<primary-abi>/lib<name>.so
في حال عدم العثور على أي منها وتحديد واجهة ABI ثانوية، تبحث الخدمة عن المكتبات المشتركة ضمن النموذج:
lib/<secondary-abi>/lib<name>.so
عندما يعثر مدير الحِزم على المكتبات التي يبحث عنها، ينسخها
إلى /lib/lib<name>.so
ضمن دليل المكتبة الأصلي للتطبيق
(<nativeLibraryDir>/
). تسترجع المقتطفات التالية nativeLibraryDir
:
Kotlin
import android.content.pm.PackageInfo import android.content.pm.ApplicationInfo import android.content.pm.PackageManager ... val ainfo = this.applicationContext.packageManager.getApplicationInfo( "com.domain.app", PackageManager.GET_SHARED_LIBRARY_FILES ) Log.v(TAG, "native library dir ${ainfo.nativeLibraryDir}")
Java
import android.content.pm.PackageInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; ... ApplicationInfo ainfo = this.getApplicationContext().getPackageManager().getApplicationInfo ( "com.domain.app", PackageManager.GET_SHARED_LIBRARY_FILES ); Log.v( TAG, "native library dir " + ainfo.nativeLibraryDir );
في حال عدم توفّر ملف عنصر مشترَك على الإطلاق، يتم إنشاء التطبيق وتثبيته، ولكن يتعطل أثناء وقت التشغيل.
ARMv9: تفعيل PAC وBTI لـ C/C++
سيوفّر تفعيل PAC/BTI الحماية من بعض جهات الهجوم. تحمي تقنية PAC عناوين الإرجاع من خلال التوقيع عليها بشكل مشفّر في ملف بدء الدالة والتحقّق من أنّ عنوان الإرجاع لا يزال موقَّعًا بشكل صحيح في ملف النهايات. يمنع BTI الانتقال إلى مواقع عشوائية في الرمز البرمجي من خلال اشتراط أن يكون كل هدف فرعي عبارة عن تعليمات خاصة لا تفعل شيئًا سوى إخبار المعالج بأنّه لا بأس بالانتقال إليه.
يستخدم Android تعليمات PAC/BTI التي لا تؤدي إلى أي إجراء على المعالجات القديمة التي لا تتوافق مع التعليمات الجديدة. ستحصل الأجهزة التي تتضمّن معالجات ARMv9 فقط على حماية PAC/BTI، ولكن يمكنك تشغيل الرمز نفسه على أجهزة ARMv8 أيضًا، بدون حاجة إلى صيغ متعددة من مكتبتك. حتى على أجهزة ARMv9، لا ينطبق PAC/BTI إلا على رمز 64 بت.
سيؤدي تفعيل PAC/BTI إلى زيادة طفيفة في حجم الرمز، عادةً بنسبة %1.
راجِع قسم التعرّف على البنية - توفير الحماية للبرامج المعقّدة (PDF) للحصول على شرح تفصيلي حول استهداف PAC/BTI لمتّجهات الهجوم، وطريقة عمل الحماية.
تغييرات الإصدار
ndk-build
يمكنك ضبط LOCAL_BRANCH_PROTECTION := standard
في كل وحدة من وحدات Android.mk.
إنشاء تصميمات الإعلانات
استخدِم target_compile_options($TARGET PRIVATE -mbranch-protection=standard)
لكل هدف في CMakeLists.txt.
أنظمة التصميم الأخرى
اجمع الرمز الخاص بك باستخدام -mbranch-protection=standard
. لا تعمل هذه العلامة إلا عند التجميع
لواجهة Arm64-v8a ABI. لست بحاجة إلى استخدام هذه العلامة عند
الربط.
تحديد المشاكل وحلّها
لا نعلم بوجود أي مشاكل في توافق المُجمِّع مع PAC/BTI، ولكن:
- احرِص على عدم خلط رمز BTI مع رمز غير متوافق معه عند الربط، لأنّ ذلك يؤدي إلى إنشاء مكتبة لا تتضمّن حماية BTI. يمكنك استخدام llvm-readelf للتحقق مما إذا كانت المكتبة الناتجة تحتوي على ملاحظة BTI أم لا.
$ llvm-readelf --notes LIBRARY.so [...] Displaying notes found in: .note.gnu.property Owner Data size Description GNU 0x00000010 NT_GNU_PROPERTY_TYPE_0 (property note) Properties: aarch64 feature: BTI, PAC [...] $
تتضمّن الإصدارات القديمة من مكتبة OpenSSL (الإصدارات الأقدم من 1.1.1i) خطأ في المجمع المكتوب يتسبب في حدوث أخطاء في معالجة بيانات اعتماد العميل. الترقية إلى أحدث إصدار من مكتبة OpenSSL
تُنشئ الإصدارات القديمة من بعض أنظمة إدارة الحقوق الرقمية للتطبيقات رمزًا ينتهك متطلبات PAC/BTI. إذا كنت تستخدم إدارة الحقوق الرقمية للتطبيقات وواجهت مشاكل عند تفعيل PAC/BTI، تواصَل مع موفِّر إدارة الحقوق الرقمية للحصول على إصدار تم إصلاحه.