ABI ของ Android

อุปกรณ์ Android แต่ละประเภทใช้ CPU ที่แตกต่างกัน จึงรองรับการใช้งานที่ต่างกันด้วย ชุดคำสั่ง CPU และชุดคำสั่งแต่ละชุดมีหน่วยของตัวเอง อินเทอร์เฟซแบบไบนารีของแอปพลิเคชัน (ABI) ABI ประกอบด้วยข้อมูลต่อไปนี้

  • ชุดคำสั่งของ CPU (และส่วนขยาย) ที่ใช้ได้
  • จุดสิ้นสุดของหน่วยความจำที่จัดเก็บและโหลดขณะรันไทม์ ใช้ Android เสมอ Little-endian
  • แบบแผนสำหรับการส่งข้อมูลระหว่างแอปพลิเคชันและระบบ รวมถึง ข้อจำกัดการจัดแนว และวิธีที่ระบบใช้สแต็กและ จะลงทะเบียนเมื่อมีการเรียกใช้ฟังก์ชัน
  • รูปแบบของไบนารีที่สั่งการได้ เช่น โปรแกรมและไลบรารีที่ใช้ร่วมกัน รวมถึงประเภทเนื้อหาที่รองรับ Android ใช้ ELF เสมอ สำหรับข้อมูลเพิ่มเติม ดูข้อมูลได้ที่ อินเทอร์เฟซแบบไบนารีของแอปพลิเคชันของระบบ ELF
  • ชื่อ C++ ทำให้สับสนได้อย่างไร สำหรับข้อมูลเพิ่มเติม โปรดดู General/Itanium C++ ABI

หน้านี้แจกแจง ABI ที่ NDK รองรับ และให้ข้อมูล เกี่ยวกับวิธีการทำงานของ ABI แต่ละรายการ

ABI ยังสามารถหมายถึง API แบบเนทีฟที่แพลตฟอร์มรองรับได้ด้วย สำหรับ รายการปัญหา ABI ประเภทที่มีผลกับระบบ 32 บิต โปรดดู ข้อบกพร่อง ABI 32 บิต

ABI ที่รองรับ

ตาราง 1 ABI และชุดคำสั่งที่รองรับ

ABI ชุดวิธีการที่รองรับ หมายเหตุ
armeabi-v7a
  • Armeabi
  • แบบ Thumb-2
  • นีออน
  • ใช้ร่วมกับอุปกรณ์ ARMv5/v6 ไม่ได้
    arm64-v8a
  • AArch64
  • Armv8.0 เท่านั้น
    x86
  • X86 (IA-32)
  • MMX
  • SSE/2/3
  • SSSE3
  • ไม่รองรับ MOVBE หรือ SSE4
    x86_64
  • X86-64
  • MMX
  • SSE/2/3
  • SSSE3
  • SSE4.1, 4.2
  • POPCNT
  • x86-64-v1 แบบเต็ม แต่มีเพียง x86-64-v2 บางส่วนเท่านั้น (ไม่มี CMPXCHG16B หรือ LAHF-SAHF)

    หมายเหตุ: ก่อนหน้านี้ NDK รองรับ ARMv5 (armeabi) และ MIPS แบบ 32 บิตและ 64 บิต แต่การรองรับ ABI เหล่านี้ถูกนําออกใน NDK r17

    Armeabi-V7a

    ABI นี้มีไว้สำหรับ CPU แบบ ARM 32 บิต โดยมี Thumb-2 และนีออน

    สำหรับข้อมูลเกี่ยวกับส่วนต่างๆ ของ ABI ที่ไม่เกี่ยวกับ Android โดยเฉพาะ โปรดดู อินเทอร์เฟซแบบไบนารีของแอปพลิเคชัน (ABI) สำหรับสถาปัตยกรรม ARM

    ระบบบิลด์ของ NDK จะสร้างรหัส Thumb-2 โดยค่าเริ่มต้น เว้นแต่คุณจะใช้ LOCAL_ARM_MODE ใน Android.mk สำหรับ ndk-build หรือ ANDROID_ARM_MODE เมื่อกำหนดค่า CMake

    ดูข้อมูลเพิ่มเติมเกี่ยวกับประวัติของ Neon ได้ที่ทีมสนับสนุนของ Neon

    สำหรับเหตุผลที่ผ่านมา ABI นี้ใช้ -mfloat-abi=softfp ที่ทําให้เกิด float ทั้งหมด ค่าที่จะส่งผ่านในการลงทะเบียนจำนวนเต็มและค่า double ทั้งหมดที่จะส่ง เป็นคู่การลงทะเบียนจำนวนเต็มเมื่อเรียกใช้ฟังก์ชัน แม้ว่าจะมีชื่อ มีผลกับแบบแผนการเรียกใช้จุดลอยตัวเท่านั้น กล่าวคือคอมไพเลอร์จะยังคง ให้ใช้คำสั่งจุดลอยตัวของฮาร์ดแวร์สำหรับเลขคณิต

    ABI นี้ใช้ long double 64 บิต (IEEEไบนารี64 เดียวกับ double)

    ARM64-V8a

    ABI นี้มีไว้สำหรับ CPU แบบ ARM 64 บิต

    ดู Arm ศึกษาสถาปัตยกรรม เพื่อดูรายละเอียดทั้งหมดเกี่ยวกับส่วนต่างๆ ของ ABI ที่ไม่ได้มีไว้สำหรับ Android กลุ่ม ยังให้คำแนะนำการย้าย การพัฒนา Android 64 บิต

    คุณใช้ Neon Intrinsics ได้ ในโค้ด C และ C++ เพื่อใช้ประโยชน์จากส่วนขยาย SIMD ขั้นสูง คู่มือของ Neon Programmer สำหรับ Armv8-A ให้ข้อมูลเพิ่มเติมเกี่ยวกับภายในของ Neon และการเขียนโปรแกรม Neon โดยทั่วไป

    สำหรับ Android การลงทะเบียน x18 เฉพาะแพลตฟอร์มนั้นสงวนไว้สำหรับ ShadowCallStack และไม่ควรสัมผัสโค้ด Clang เวอร์ชันปัจจุบันมีค่าเริ่มต้นเป็น โดยใช้ตัวเลือก -ffixed-x18 ใน Android ดังนั้น นอกจากคุณจะเขียนด้วยลายมือ Assistant (หรือคอมไพเลอร์ที่เก่ามาก) คุณไม่ควรกังวลในเรื่องนี้

    ABI นี้ใช้ long double แบบ 128 บิต (ไบนารี IEEE128)

    X86

    ABI นี้มีไว้สำหรับ CPU ที่รองรับชุดคำสั่งที่เรียกกันโดยทั่วไปว่า "x86" "i386" หรือ "IA-32"

    ABI ของ Android มีชุดคำสั่งพื้นฐานบวก MMX SSE SSE2 SSE3 และ ส่วนขยาย SSSE3

    ABI ไม่รวมชุดคำสั่ง IA-32 อื่นๆ ส่วนขยาย เช่น MOVBE หรือ SSE4 เวอร์ชันใดก็ตาม คุณจะยังใช้ส่วนขยายเหล่านี้ได้ ตราบใดที่คุณใช้การตรวจสอบฟีเจอร์รันไทม์เพื่อ เปิดใช้งานและแสดงรายการสำรองสำหรับอุปกรณ์ที่ไม่รองรับ

    เครื่องมือเชน NDK จะถือว่ามีการจัดแนวสแต็ก 16 ไบต์ก่อนการเรียกใช้ฟังก์ชัน เครื่องมือเริ่มต้นและ จะบังคับใช้กฎนี้ หากกำลังเขียนโค้ดการประกอบ คุณต้องตรวจสอบว่าได้ดูแลรักษาสแต็ก ความสอดคล้อง และตรวจสอบว่าคอมไพเลอร์อื่นๆ ปฏิบัติตามกฎนี้ด้วย

    โปรดดูรายละเอียดเพิ่มเติมในเอกสารต่อไปนี้

    ABI นี้ใช้ long double แบบ 64 บิต (IEEEไบนารี64 เหมือนกับ double และไม่ได้ใช้มากกว่า Intel-only 80 บิต long double ทั่วไป)

    X86_64

    ABI นี้มีไว้สำหรับ CPU ที่รองรับชุดคำสั่งที่เรียกกันโดยทั่วไปว่า "x86-64"

    ABI ของ Android มีชุดคำสั่งพื้นฐานบวก MMX SSE SSE2 SSE3 SSSE3 SSE4.1 SSE4.2 และ คำสั่ง POPCNT

    ABI ไม่รวมชุดคำสั่ง x86-64 อื่นๆ ส่วนขยาย เช่น MOVBE, SHA หรือ AVX เวอร์ชันใดก็ตาม คุณจะยังใช้ส่วนขยายเหล่านี้ได้ ตราบใดที่คุณใช้ฟีเจอร์รันไทม์ที่ตรวจสอบ เปิดใช้งานและแสดงรายการสำรองสำหรับอุปกรณ์ที่ไม่รองรับ

    โปรดดูรายละเอียดเพิ่มเติมในเอกสารต่อไปนี้

    ABI นี้ใช้ long double แบบ 128 บิต (ไบนารี IEEE128)

    สร้างโค้ดสำหรับ ABI ที่เฉพาะเจาะจง

    เกรเดิล

    Gradle (ไม่ว่าจะใช้ผ่าน Android Studio หรือจากบรรทัดคำสั่ง) สร้างสำหรับ ABI ทั้งหมดที่ยังไม่เลิกใช้งานโดยค่าเริ่มต้น เพื่อจำกัดชุดของ ABI ที่ แอปพลิเคชันรองรับ ใช้ abiFilters ตัวอย่างเช่น เพื่อสร้าง ABI 64 บิต ให้ตั้งค่าการกำหนดค่าต่อไปนี้ใน build.gradle

    android {
        defaultConfig {
            ndk {
                abiFilters 'arm64-v8a', 'x86_64'
            }
        }
    }
    

    Ndk-Build

    โดยค่าเริ่มต้น บิลด์ ndk-build สำหรับ ABI ทั้งหมดที่ยังไม่ได้เลิกใช้งาน คุณสามารถกำหนดเป้าหมาย ABI ที่ระบุโดยการตั้งค่า 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 คุณสามารถสร้างสำหรับ 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 แบบอ้วนมีขนาดใหญ่กว่ามาก มากกว่า 1 รายการที่มีเฉพาะไบนารีสำหรับ ABI เดียว ข้อดีจะมาตอบแทน ความเข้ากันได้ที่กว้างขึ้น แต่ก็ต้องแลกกับ APK ที่ใหญ่กว่า สำคัญมาก ขอแนะนำให้คุณใช้ประโยชน์จาก App Bundle หรือการแยก APK เพื่อ ลดขนาด APK ในขณะที่ยังคงรักษาอุปกรณ์จำนวนสูงสุดไว้ ความสามารถในการใช้งานร่วมกัน

    เมื่อทำการติดตั้ง ตัวจัดการแพ็กเกจจะคลายการแพคข้อมูลเฉพาะ รหัสเครื่องสำหรับอุปกรณ์เป้าหมาย โปรดดูรายละเอียดที่หัวข้อการดึงข้อมูลโดยอัตโนมัติ โค้ดเนทีฟในเวลาติดตั้ง

    การจัดการ ABI บนแพลตฟอร์ม Android

    ส่วนนี้จะให้รายละเอียดเกี่ยวกับวิธีที่แพลตฟอร์ม Android จัดการโฆษณาเนทีฟ ใน APK

    โค้ดที่มาพร้อมเครื่องในแพ็กเกจแอป

    ทั้ง Play Store และตัวจัดการแพ็กเกจคาดหวังว่าจะพบ NDK ที่ผลิตขึ้น ไลบรารีบนเส้นทางไฟล์ภายใน APK ที่ตรงกับรูปแบบต่อไปนี้

    /lib/<abi>/lib<name>.so
    

    ในที่นี้ <abi> เป็นหนึ่งในชื่อ ABI ที่แสดงภายใต้ ABI ที่รองรับ และ <name> คือชื่อของไลบรารีตามที่คุณกำหนดสำหรับ LOCAL_MODULE ในไฟล์ Android.mk ตั้งแต่ปี ไฟล์ APK จะเป็นแค่ไฟล์ ZIP การเปิดไฟล์เหล่านี้และยืนยันว่าโฆษณาเนทีฟที่แชร์ ห้องสมุดคือที่ที่พวกเขาอยู่

    หากระบบไม่พบไลบรารีที่แชร์แบบเนทีฟในตำแหน่งที่คาดไว้ ไลบรารีจะใช้ ให้พวกเขา ในกรณีดังกล่าว แอปจะต้องคัดลอกไลบรารีมา จากนั้น ดำเนินการ dlopen()

    ใน APK ไฟล์ขนาดใหญ่ ไลบรารีแต่ละรายการจะอยู่ในไดเรกทอรีที่ชื่อตรงกับ 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 จำนวนมากเรียกใช้ไบนารี NDK armeabi-v7a และ armeabi ได้ด้วย สำหรับ อุปกรณ์ดังกล่าว ABI หลักจะเป็น x86 และอุปกรณ์ที่ 2 คือ 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 ปกป้องที่อยู่สำหรับส่งกลับโดยการลงชื่อที่อยู่เหล่านั้นแบบเข้ารหัสในฟังก์ชัน Prolog และตรวจสอบว่าที่อยู่สำหรับคืนสินค้ายังคงมีการลงชื่อเข้าใช้อย่างถูกต้อง บทสรุป BTI ป้องกันการข้ามไปยังตำแหน่งที่กำหนดเองในโค้ดของคุณโดยกำหนดให้ แต่ละเป้าหมายสาขาเป็นคำสั่งพิเศษที่ไม่ทำอะไรนอกจากบอก โปรเซสเซอร์ว่า คุณสามารถลงจอดที่นั่นได้

    Android ใช้วิธีการของ PAC/BTI ซึ่งไม่ดำเนินการใดๆ กับหน่วยประมวลผลรุ่นเก่าที่ ไม่สนับสนุนวิธีการใหม่ เฉพาะอุปกรณ์ ARMv9 เท่านั้นที่จะมี PAC/BTI แต่คุณสามารถเรียกใช้โค้ดเดียวกันนี้บนอุปกรณ์ ARMv8 ได้เช่นกัน โดยไม่จำเป็นต้อง ไลบรารีรูปแบบต่างๆ แม้จะในอุปกรณ์ ARMv9 แต่ระบบจะใช้ PAC/BTI เท่านั้น เป็นรหัส 64 บิต

    การเปิดใช้ PAC/BTI จะทำให้ขนาดโค้ดเพิ่มขึ้นเล็กน้อย ซึ่งโดยปกติแล้วจะอยู่ที่ 1%

    ดู Arm's เรียนรู้สถาปัตยกรรม - ให้การปกป้องสำหรับ ซอฟต์แวร์ที่ซับซ้อน (PDF) เพื่อดูคำอธิบายโดยละเอียดเกี่ยวกับเวกเตอร์การโจมตีเป้าหมาย PAC/BTI และ การป้องกันจึงได้ผล

    สร้างการเปลี่ยนแปลง

    Ndk-Build

    ตั้งค่า LOCAL_BRANCH_PROTECTION := standard ในแต่ละโมดูลของ Android.mk

    ผู้ผลิต

    ใช้ target_compile_options($TARGET PRIVATE -mbranch-protection=standard) สำหรับแต่ละเป้าหมายใน CMakeLists.txt

    ระบบบิลด์อื่นๆ

    คอมไพล์โค้ดโดยใช้ -mbranch-protection=standard แฟล็กนี้ใช้งานได้เท่านั้น เมื่อคอมไพล์สำหรับ aBI ที่ arm64-v8a คุณไม่จําเป็นต้องใช้แฟล็กนี้เมื่อ และการลิงก์

    การแก้ปัญหา

    เราไม่พบปัญหาใดๆ เกี่ยวกับการรองรับคอมไพเลอร์สำหรับ PAC/BTI แต่

    • โปรดระวังอย่ารวมรหัส 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) มีข้อบกพร่องใน Ascyclr ที่เขียนด้วยลายมือ ที่ทำให้ PAC ล้มเหลว อัปเกรดเป็น OpenSSL ปัจจุบัน

    • ระบบ DRM ของแอปเวอร์ชันเก่าบางระบบสร้างโค้ดที่ละเมิด PAC/BTI หากคุณใช้ DRM ของแอปและพบปัญหาเมื่อเปิดใช้ PAC/BTI โปรดติดต่อผู้ให้บริการ DRM เพื่อขอเวอร์ชันที่แก้ไขแล้ว