বিভিন্ন Android ডিভাইস বিভিন্ন CPU ব্যবহার করে, যা বিভিন্ন ইনস্ট্রাকশন সেট সাপোর্ট করে। CPU ও ইনস্ট্রাক্টর সেটের প্রতিটি কম্বিনেশনের নিজস্ব অ্যাপ্লিকেশন বাইনারি ইন্টারফেস (ABI) থাকে। ABI-তে নিম্নলিখিত তথ্য থাকে:
- ব্যবহার করা যেতে পারে এমন CPU ইনস্ট্রাকশন সেট (এবং এক্সটেনশন)।
- রানটাইমে মেমরি স্টোর ও লোডের এন্ডিয়াননেস। Android সবসময় লিটল-এন্ডিয়ান।
- অ্যাপ্লিকেশন এবং সিস্টেমের মধ্যে ডেটা পাস করার কনভেনশন, যার মধ্যে রয়েছে অ্যালাইনমেন্ট কনস্ট্রেন্ট এবং সিস্টেম কীভাবে স্ট্যাক ব্যবহার করে এবং ফাংশন কল করার সময় রেজিস্টার করে।
- প্রোগ্রাম ও শেয়ার করা লাইব্রেরির মতো এক্সিকিউটেবল বাইনারির ফর্ম্যাট এবং এগুলি যে ধরনের কন্টেন্ট সাপোর্ট করে। Android সবসময় ELF ব্যবহার করে। আরও তথ্যের জন্য, ELF System V অ্যাপ্লিকেশন বাইনারি ইন্টারফেস দেখুন।
- C++ নাম কীভাবে ম্যাঙ্গেল করা হয়। আরও তথ্যের জন্য, Generic/Itanium C++ ABI দেখুন।
এই পৃষ্ঠায় NDK-তে কাজ করে এমন ABI-এর তালিকা দেওয়া হয়েছে এবং প্রতিটি ABI কীভাবে কাজ করে সেই সম্পর্কে তথ্য দেওয়া হয়েছে।
এছাড়াও, ABI বলতে প্ল্যাটফর্মের মাধ্যমে কাজ করে এমন নেটিভ API-কে বোঝাতে পারে। ৩২-বিট সিস্টেমকে প্রভাবিত করে এমন ABI সংক্রান্ত সমস্যার তালিকা দেখতে, ৩২-বিট ABI বাগ দেখুন।
কাজ করে এমন ABI
টেবিল ১. ABI এবং কাজ করে এমন ইনস্ট্রাকশন সেট।
| ABI | কাজ করে এমন ইনস্ট্রাকশন সেট | নোট |
|---|---|---|
armeabi-v7a |
|
ARMv5/v6 ডিভাইসের সাথে মানানসই নয়। |
arm64-v8a |
শুধু Armv8.0। | |
x86 |
MOVBE বা SSE4-এর জন্য কোনও সহায়তা নেই। | |
x86_64 |
|
সম্পূর্ণ x86-64-v2। |
মনে রাখবেন: আগে NDK-তে ARMv5 (armeabi) এবং ৩২-বিট ও ৬৪-বিট MIPS কাজ করত, কিন্তু NDK r17-এ এইসব ABI-এর জন্য সহায়তা সরিয়ে দেওয়া হয়েছে।
armeabi-v7a
এই ABI ৩২-বিট ARM CPU-এর জন্য। এতে Thumb-2 এবং Neon অন্তর্ভুক্ত।
ABI-এর যেসব অংশ Android-নির্দিষ্ট নয়, সেই সম্পর্কে তথ্যের জন্য, ARM আর্কিটেকচারের জন্য অ্যাপ্লিকেশন বাইনারি ইন্টারফেস (ABI) দেখুন
NDK-এর বিল্ড সিস্টেম ডিফল্ট হিসেবে থাম্ব-২ কোড জেনারেট করে, যদি না আপনি
ndk-বিল্ডের জন্য Android.mk-এ LOCAL_ARM_MODE অথবা CMake কনফিগার করার সময় ANDROID_ARM_MODE ব্যবহার করেন।
Neon-এর ইতিহাস সম্পর্কে আরও জানতে, Neon সহায়তা দেখুন।
ঐতিহাসিক কারণে, এই ABI -mfloat-abi=softfp ব্যবহার করে যার ফলে ফাংশন কল করার সময় সব float
ভ্যালু ইন্টিজার রেজিস্টারে এবং সব double ভ্যালু ইন্টিজার রেজিস্টার পেয়ারে
পাস করা হয়। নাম যাই হোক না কেন, এটি
শুধুমাত্র ফ্লোটিং পয়েন্ট কলিং কনভেনশনকে প্রভাবিত করে: কম্পাইলার এখনও
অ্যারিথমেটিকের জন্য হার্ডওয়্যার ফ্লোটিং পয়েন্ট নির্দেশাবলী ব্যবহার করবে।
এই ABI একটি 64-বিট long double (IEEE binary64 double-এর মতো একই) ব্যবহার করে।
arm64-v8a
এই ABI হল ৬৪-বিট ARM CPU-এর জন্য।
ABI-এর যেসব অংশ Android-নির্দিষ্ট নয়, সেগুলির সম্পূর্ণ বিবরণ পেতে Arm-এর আর্কিটেকচার সম্পর্কে জানুন দেখুন। Arm এছাড়াও ৬৪-বিট Android ডেভেলপমেন্ট-এ কিছু পোর্টিং সংক্রান্ত পরামর্শ দেয়।
উন্নত SIMD এক্সটেনশনের সুবিধা নিতে আপনি C এবং C++ কোডে Neon intrinsics ব্যবহার করতে পারেন। Neon Programmer's Guide for Armv8-A থেকে Neon ইন্ট্রিনসিক ও Neon প্রোগ্রামিং সম্পর্কে আরও তথ্য পাবেন।
Android-এ, প্ল্যাটফর্ম-নির্দিষ্ট x18 রেজিস্টার
ShadowCallStack-এর জন্য
সংরক্ষিত এবং আপনার কোড দ্বারা স্পর্শ করা উচিত নয়। Clang-এর বর্তমান ভার্সন ডিফল্ট হিসেবে
Android-এ -ffixed-x18 বিকল্প ব্যবহার করে, তাই আপনার কাছে হাতে লেখা
অ্যাসেম্বলার (বা খুব পুরনো কম্পাইলার) না থাকলে, আপনাকে এই বিষয়ে চিন্তা করতে হবে না।
এই ABI ১২৮-বিট long double (IEEE binary128) ব্যবহার করে।
x86
এই ABI হল সেইসব CPU-এর জন্য যা সাধারণত "x86", "i386" বা "IA-32" নামে পরিচিত ইনস্ট্রাকশন সেটকে সাপোর্ট করে।
Android-এর ABI-তে বেস ইনস্ট্রাকশন সেট এবং MMX, SSE, SSE2, SSE3 এবং SSSE3 এক্সটেনশন অন্তর্ভুক্ত।
ABI-তে MOVBE বা SSE4-এর যেকোনও ভেরিয়েন্টের মতো অন্য কোনও ঐচ্ছিক IA-32 ইনস্ট্রাকশন সেট এক্সটেনশন অন্তর্ভুক্ত নেই। আপনি এখনও এইসব এক্সটেনশন ব্যবহার করতে পারবেন, যতক্ষণ না আপনি রানটাইম ফিচার-প্রোবিং ব্যবহার করে সেগুলি চালু করছেন এবং যেসব ডিভাইসে সেগুলি কাজ করে না সেগুলির জন্য ফলব্যাক প্রদান করছেন।
NDK টুলচেন ধরে নেয় যে ফাংশন কলের আগে ১৬-বাইট স্ট্যাক অ্যালাইনমেন্ট আছে। ডিফল্ট টুল ও বিকল্প এই নিয়ম প্রয়োগ করে। আপনি অ্যাসেম্বলি কোড লিখলে, আপনাকে অবশ্যই স্ট্যাক অ্যালাইনমেন্ট বজায় রাখতে হবে এবং অন্যান্য কম্পাইলারও এই নিয়ম মেনে চলছে কিনা তা নিশ্চিত করতে হবে।
আরও বিবরণের জন্য নিচে দেওয়া ডকুমেন্ট দেখুন:
- বিভিন্ন C++ কম্পাইলার ও অপারেটিং সিস্টেমের জন্য কলিং কনভেনশন
- Intel IA-32 Intel আর্কিটেকচার সফ্টওয়্যার ডেভেলপারের ম্যানুয়াল, ভলিউম ২: ইনস্ট্রাকশন সেট রেফারেন্স
- Intel IA-32 Intel আর্কিটেকচার সফ্টওয়্যার ডেভেলপারের ম্যানুয়াল, ভলিউম ৩: সিস্টেম প্রোগ্রামিং গাইড
- System V Application Binary Interface: Intel386 Processor Architecture Supplement
এই ABI একটি ৬৪-বিট long double (IEEE binary64 double-এর মতো একই, এবং আরও বেশি নয়
সাধারণ ৮০-বিট Intel-only long double) ব্যবহার করে।
x86_64
এই ABI হল সেইসব CPU-এর জন্য যেগুলি সাধারণত "x86-64-v2" হিসেবে পরিচিত ইনস্ট্রাকশন সেটকে সাপোর্ট করে।
Android-এর ABI-তে বেস ইনস্ট্রাকশন সেট এবং MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2 এবং POPCNT ইনস্ট্রাকশন অন্তর্ভুক্ত থাকে।
ABI-তে MOVBE, SHA বা AVX-এর যেকোনও ভ্যারিয়েন্টের মতো অন্য কোনও ঐচ্ছিক x86-64 ইনস্ট্রাকশন সেট এক্সটেনশন অন্তর্ভুক্ত নেই। আপনি এখনও এইসব এক্সটেনশন ব্যবহার করতে পারবেন, যতক্ষণ না আপনি রানটাইম ফিচার প্রোবিং ব্যবহার করে সেগুলি চালু করছেন এবং যেসব ডিভাইসে সেগুলি কাজ করে না সেগুলির জন্য ফলব্যাক প্রদান করছেন।
আরও বিবরণের জন্য নিচে দেওয়া ডকুমেন্ট দেখুন:
- বিভিন্ন C++ কম্পাইলার ও অপারেটিং সিস্টেমের জন্য কলিং কনভেনশন
- Intel64 এবং IA-32 আর্কিটেকচার সফ্টওয়্যার ডেভেলপারের ম্যানুয়াল, ভলিউম ২: ইনস্ট্রাকশন সেট রেফারেন্স
- Intel64 and IA-32 Intel Architecture Software Developer's Manual Volume 3: System Programming
এই ABI ১২৮-বিট long double (IEEE binary128) ব্যবহার করে।
নির্দিষ্ট ABI-এর জন্য কোড জেনারেট করুন
Gradle
Gradle (Android Studio বা কমান্ড লাইনের মাধ্যমে ব্যবহার করা হোক না কেন) ডিফল্ট হিসেবে
বন্ধ করে দেওয়া হয়নি এমন সব ABI-এর জন্য বিল্ড করে। আপনার অ্যাপ্লিকেশন সাপোর্ট করে এমন ABI-এর সেট সীমাবদ্ধ করতে, abiFilters ব্যবহার করুন। যেমন, শুধুমাত্র ৬৪-বিট ABI-এর জন্য তৈরি করতে, আপনার build.gradle-এ নিম্নলিখিত কনফিগারেশন সেট করুন:
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'x86_64'
}
}
}
ndk-build
ndk-build ডিফল্ট হিসেবে সব নন-ডেপ্রিকেটেড ABI-এর জন্য বিল্ড করে। আপনি Application.mk ফাইলে APP_ABI সেট করে নির্দিষ্ট ABI টার্গেট করতে পারবেন। নিচের
স্নিপেটে 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 ...
NDK-এর সাহায্যে বিল্ড করার জন্য CMake-এ পাস করতে হবে এমন অন্যান্য ফ্ল্যাগের জন্য, CMake গাইড দেখুন।
বিল্ড সিস্টেমের ডিফল্ট অ্যাকশন হল প্রতিটি ABI-এর জন্য বাইনারি একটি APK-তে অন্তর্ভুক্ত করা, যা ফ্যাট APK নামেও পরিচিত। একটি ফ্যাট APK, একটি ABI-এর জন্য শুধুমাত্র বাইনারি থাকা APK-এর চেয়ে উল্লেখযোগ্যভাবে বড়; এর সুবিধা হল এটি আরও বেশি ডিভাইসের সাথে মানানসই, তবে এর জন্য APK-এর সাইজ বড় হয়। সর্বাধিক ডিভাইস মানানসইতা বজায় রেখে আপনার APK-র সাইজ কমাতে, আপনাকে অ্যাপ বান্ডেল অথবা APK স্প্লিট-এর সুবিধা নেওয়ার জন্য বিশেষভাবে সাজেস্ট করা হচ্ছে।
ইনস্টলেশনের সময়, প্যাকেজ ম্যানেজার টার্গেট ডিভাইসের জন্য শুধুমাত্র সবচেয়ে উপযুক্ত মেশিন কোড আনপ্যাক করে। আরও তথ্যের জন্য, ইনস্টল করার সময় নেটিভ কোড অটোমেটিক এক্সট্র্যাক্ট করা দেখুন।
Android প্ল্যাটফর্মে ABI ম্যানেজমেন্ট
এই বিভাগে Android প্ল্যাটফর্ম কীভাবে APK-তে নেটিভ কোড ম্যানেজ করে সেই সম্পর্কে বিবরণ দেওয়া হয়েছে।
অ্যাপ প্যাকেজে নেটিভ কোড
Play Store এবং প্যাকেজ ম্যানেজার, উভয়ই আশা করে যে APK-এর মধ্যে থাকা ফাইলপাথে NDK-জেনারেটেড লাইব্রেরি খুঁজে পাওয়া যাবে যা নিম্নলিখিত প্যাটার্নের সাথে ম্যাচ করে:
/lib/<abi>/lib<name>.so
এখানে, <abi> হল কাজ করে এমন ABI-এর অধীনে তালিকাভুক্ত ABI নামগুলির মধ্যে একটি,
এবং <name> হল লাইব্রেরির নাম, যেমন আপনি Android.mk ফাইলে LOCAL_MODULE
ভেরিয়েবলের জন্য এটি সংজ্ঞায়িত করেছেন। যেহেতু
APK ফাইলগুলি কেবল জিপ ফাইল, তাই সেগুলি খোলা এবং শেয়ার করা নেটিভ
লাইব্রেরিগুলি যেখানে আছে তা নিশ্চিত করা তুচ্ছ।
সিস্টেম যদি নেটিভ শেয়ার করা লাইব্রেরিগুলি যেখানে আশা করে সেখানে খুঁজে না পায়, তাহলে এটি ব্যবহার করতে পারবে না। এই ধরনের ক্ষেত্রে, অ্যাপকেই লাইব্রেরি কপি করতে হবে এবং তারপর
dlopen() করতে হবে।
ফ্যাট APK-তে, প্রতিটি লাইব্রেরি এমন একটি ডিরেক্টরির অধীনে থাকে যার নাম সংশ্লিষ্ট ABI-এর সাথে মেলে। যেমন, একটি ফ্যাট APK-তে এগুলি থাকতে পারে:
/lib/armeabi-v7a/libfoo.so /lib/arm64-v8a/libfoo.so /lib/x86/libfoo.so /lib/x86_64/libfoo.so
Android প্ল্যাটফর্ম ABI সহায়তা
Android সিস্টেম রানটাইমে জানে যে এটি কোন ABI(গুলি) সমর্থন করে, কারণ বিল্ড-নির্দিষ্ট সিস্টেম বৈশিষ্ট্যগুলি নির্দেশ করে:
- ডিভাইসের জন্য প্রাথমিক ABI, সিস্টেম ইমেজে ব্যবহৃত মেশিন কোডের সাথে সম্পর্কিত।
- বিকল্প হিসেবে, সেকেন্ডারি ABI, যা সিস্টেম ইমেজের সাথে কাজ করে এমন অন্য ABI-এর সাথে সম্পর্কিত।
এই মেকানিজম নিশ্চিত করে যে সিস্টেম ইনস্টলেশনের সময় প্যাকেজ থেকে সেরা মেশিন কোড এক্সট্র্যাক্ট করে।
আপনি নির্দিষ্ট ABI-এর জন্য 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}")
জাভা
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: C/C++-এর জন্য PAC ও BTI চালু করা
PAC/BTI চালু করলে কিছু অ্যাটাক ভেক্টরের বিরুদ্ধে সুরক্ষা পাওয়া যাবে। PAC কোনও ফাংশনের প্রোলগে রিটার্ন অ্যাড্রেস ক্রিপ্টোগ্রাফিকভাবে সাইন করে এবং এপিলোগে রিটার্ন অ্যাড্রেস এখনও সঠিকভাবে সাইন করা আছে কিনা তা চেক করে রিটার্ন অ্যাড্রেস সুরক্ষিত রাখে। BTI আপনার কোডের যেকোনও লোকেশনে জাম্প করা আটকায়। এর জন্য প্রতিটি ব্রাঞ্চ টার্গেটকে একটি বিশেষ নির্দেশ হতে হয় যা প্রসেসরকে শুধু এইটুকু বলে যে সেখানে ল্যান্ড করা যেতে পারে।
Android-এ PAC/BTI নির্দেশাবলী ব্যবহার করা হয় যা পুরনো প্রসেসরে কোনও কাজ করে না, কারণ সেগুলি নতুন নির্দেশাবলী সাপোর্ট করে না। শুধুমাত্র ARMv9 ডিভাইসে PAC/BTI সুরক্ষা থাকবে, তবে আপনি ARMv8 ডিভাইসেও একই কোড চালাতে পারবেন: আপনার লাইব্রেরির একাধিক ভেরিয়েন্টের প্রয়োজন নেই। এমনকি ARMv9 ডিভাইসেও, PAC/BTI শুধুমাত্র 64-বিট কোডের ক্ষেত্রে প্রযোজ্য।
PAC/BTI চালু করলে কোডের সাইজ সামান্য বৃদ্ধি পাবে, সাধারণত ১%।
PAC/BTI যে অ্যাটাক ভেক্টরকে টার্গেট করে এবং কীভাবে সুরক্ষা কাজ করে তার বিস্তারিত ব্যাখ্যার জন্য Arm-এর আর্কিটেকচার সম্পর্কে জানুন - জটিল সফ্টওয়্যারের জন্য সুরক্ষা প্রদান করা (PDF) দেখুন।
পরিবর্তন তৈরি করুন
ndk-build
আপনার Android.mk-এর প্রতিটি মডিউলে LOCAL_BRANCH_PROTECTION := standard সেট করুন।
CMake
আপনার CMakeLists.txt-এ প্রতিটি টার্গেটের জন্য
target_compile_options($TARGET PRIVATE -mbranch-protection=standard) ব্যবহার করুন।
অন্যান্য বিল্ড সিস্টেম
-mbranch-protection=standard ব্যবহার করে আপনার কোড কম্পাইল করুন। এই ফ্ল্যাগ শুধুমাত্র
arm64-v8a ABI-এর জন্য কম্পাইল করার সময় কাজ করে। লিঙ্ক করার সময় আপনাকে এই ফ্ল্যাগ ব্যবহার করতে হবে না।
সমস্যার সমাধান
PAC/BTI-এর জন্য কম্পাইলার সাপোর্ট সংক্রান্ত কোনও সমস্যা সম্পর্কে আমরা অবগত নই, তবে:
- লিঙ্ক করার সময় BTI এবং নন-BTI কোড যাতে মিশে না যায় সেই ব্যাপারে সতর্ক থাকুন, কারণ এর ফলে এমন লাইব্রেরি তৈরি হয় যাতে BTI সুরক্ষা চালু থাকে না। আপনার তৈরি করা লাইব্রেরিতে BTI নোট আছে কিনা তা চেক করতে llvm-readelf ব্যবহার করতে পারেন।
$ 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-এর পুরনো ভার্সনে (১.১.১i-এর আগের) হ্যান্ড-রিটেন অ্যাসেম্বলারে একটি বাগ আছে যার কারণে PAC ব্যর্থ হয়। বর্তমান OpenSSL-এ আপগ্রেড করুন।
কিছু অ্যাপ DRM সিস্টেমের পুরনো ভার্সন এমন কোড তৈরি করে যা PAC/BTI প্রয়োজনীয়তা লঙ্ঘন করে। আপনি যদি অ্যাপ DRM ব্যবহার করেন এবং PAC/BTI চালু করার সময় সমস্যা দেখতে পান, তাহলে একটি নির্দিষ্ট ভার্সনের জন্য আপনার DRM ভেন্ডরের সাথে যোগাযোগ করুন।