다양한 Android 핸드셋에 다양한 CPU가 사용되는데, 이러한 CPU는 각기 다른 명령 집합을 지원합니다. CPU와 명령 집합의 조합마다 각각 고유의 ABI(애플리케이션 바이너리 인터페이스)가 있습니다. ABI는 애플리케이션의 기계어 코드가 런타임에 시스템과 상호작용하는 방식을 매우 정밀하게 정의합니다. 여러분의 앱이 사용할 각 CPU 아키텍처에 대해 ABI를 지정해야 합니다.
전형적인 ABI는 다음과 같은 정보를 포함합니다.
- 기계어 코드가 사용해야 하는 CPU 명령 집합
- 런타임에 메모리 저장 및 로드의 엔디언
- 실행 가능한 바이너리의 형식(예: 프로그램 및 공유 라이브러리)과 이러한 바이너리가 지원하는 콘텐츠의 유형
- 코드와 시스템 간에 데이터를 전달하기 위한 다양한 규칙. 이러한 규칙에는 정렬 제약 조건뿐 아니라, 시스템에서 함수를 호출할 때 스택과 레지스터를 사용하는 방법이 포함됩니다.
- 런타임에 기계어 코드에 사용 가능한 함수 기호의 목록 (일반적으로 매우 구체적인 라이브러리 집합에서 비롯됨)
이 페이지에는 NDK가 지원하는 ABI가 열거되어 있고, 각 ABI의 작동 방식에 대한 정보가 나와 있습니다.
지원되는 ABI
각각의 ABI는 하나 이상의 명령 집합을 지원합니다. 표 1에는 각 ABI가 지원하는 명령 집합의 간략한 개요가 나와 있습니다.
ABI | 지원되는 명령 집합 | 참고 |
---|---|---|
armeabi | 하드웨어 부동 소수점 없음. | |
armeabi-v7a | ARMv5, v6 기기와 호환 안 됨. | |
arm64-v8a | ||
x86 | MOVBE 또는 SSE4에 대한 지원 없음. | |
x86_64 | ||
mips | 하드웨어 부동 소수점을 사용하고 최대의 호환성을 위해 CPU:FPU 클록 비가 2:1인 것으로 가정합니다. micromips도, MIPS16도 제공하지 않습니다. | |
mips64 |
각 ABI에 대한 자세한 내용은 아래에 나와 있습니다.
armeabi
이 ABI는 ARMv5TE 명령 집합 이상을 지원하는 ARM 기반 CPU를 위한 것입니다. 자세한 내용은 다음 문서를 참조하세요.
- ARM 아키텍처 참조 매뉴얼
- ARM 아키텍처에 대한 프로시저 호출 표준
- ARM ELF 파일 형식
- ARM 아키텍처용 ABI(애플리케이션 바이너리 인터페이스)
- ARM 아키텍처용 기본 플랫폼 ABI
- ARM 아키텍처용 C 라이브러리 ABI
- ARM 아키텍처용 C++ ABI
- ARM 아키텍처용 런타임 ABI
- ELF 시스템 V 애플리케이션 바이너리 인터페이스
- Generic/Itanium C++ ABI
AAPCS 표준에서는 EABI를 유사하지만 별개의 ABI로 구성된 제품군으로 정의합니다. 또한, Android는 리틀 엔디언 ARM GNU/Linux ABI를 따릅니다.
이 ABI는 하드웨어 지원 부동 소수점 계산을 지원하지 않습니다. 그 대신, 모든 부동 소수점 연산에서는 컴파일러의 libgcc.a
정적 라이브러리에서 소프트웨어 도우미 함수를 사용합니다.
armeabi ABI는 ARM의 Thumb(Thumb-1이라고도 함) 명령 집합을 지원합니다. Android.mk
파일에서 LOCAL_ARM_MODE
변수를 사용하여 다른 동작을 지정하지 않는 한, NDK는 기본적으로 Thumb 코드를 생성합니다.
armeabi-v7a
이 ABI는 armeabi를 확장하여 여러 CPU 명령 집합 확장을 포함합니다. 이 Android 고유의 ABI가 지원하는 명령 확장은 다음과 같습니다.
- Thumb-1과 유사한 간결성으로 32비트 ARM 명령에 필적하는 성능을 제공하는 Thumb-2 명령 집합 확장.
- VFP 하드웨어 FPU 명령. 더 구체적으로는, ARM 코어에서 다른 16개의 32비트 레지스터 외에 16개의 전용 64비트 부동 소수점 레지스터를 포함하는 VFPv3-D16.
Advanced SIMD(NEON이라고도 함), VFPv3-D32 및 ThumbEE를 포함하여, v7-a ARM 사양에 기술되어 있는 다른 확장 기능들은 이 ABI에 대한 선택 사항입니다. 이러한 확장 프로그램이 있을 것이라고 보장하지 못하므로, 시스템은 런타임에 확장 프로그램을 사용할 수 있는지 검사해야 합니다. 사용할 수 없다면, 다른 코드 경로를 사용해야 합니다. 이 검사 작업은 시스템이 MMX, SSE2, 그리고 x86 CPU의 다른 특수한 명령 집합을 확인하거나 사용하기 위해 일반적으로 수행하는 검사 작업과 유사합니다.
이러한 런타임 검사를 수행하는 자세한 방법은 cpufeatures
라이브러리를 참조하세요. 또한, NEON용 기계어 코드 빌드를 위한 NDK의 지원에 대한 자세한 내용은 NEON 지원을 참조하세요.
armeabi-v7a
ABI는 -mfloat-abi=softfp
스위치를 사용하여, 함수 호출 중에 컴파일러가 전용 부동 소수점 값 대신 코어 레지스터 쌍의 모든 double 값을 전달해야 한다는 규칙을 적용합니다. 시스템은 FP 레지스터를 사용하여 모든 내부 계산을 수행할 수 있습니다. 그렇게 하면 계산 속도가 대폭 빨라집니다.
arm64-v8a
이 ABI는 AArch64를 지원하는 ARMv8 기반 CPU를 위한 것입니다. 이 ABI에는 NEON 및 VFPv4 명령 집합도 포함됩니다.
추가 정보는 ARMv8 기술 미리보기를 참조하시고, 더 자세한 사항은 ARM에 문의하세요.
x86
이 ABI는 보통 "x86" 또는 "IA-32"라고 하는 명령 집합을 지원하는 CPU를 위한 것입니다. 이 ABI의 특징은 다음과 같습니다.
- 보통 다음과 같은 컴파일러 플래그가 있는 GCC에 의해 생성되는 명령
-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32
이러한 플래그는 MMX, SSE, SSE2, SSE3 및 SSSE3 명령 집합 확장과 함께 Pentium Pro 명령 집합을 대상으로 합니다. 생성되는 코드는 최고의 Intel 32비트 CPU 전반에 균형을 맞춰 최적화된 코드입니다.
(특히 성능 최적화와 관련된) 컴파일러 플래그에 대한 자세한 내용은 GCC x86 성능 힌트를 참조하세요.
- SVR에 대한 규칙과는 반대로, 표준 Linux x86 32비트 호출 규칙 사용. 자세한 내용은 다양한 C++ 컴파일러 및 운영체제의 호출 규칙의 섹션 6, "레지스터 사용"을 참조하세요.
ABI는 다음과 같이 다른 선택적 IA-32 명령 집합 확장은 포함하지 않습니다.
- MOVBE
- SSE4의 변형
이러한 확장 기능을 활성화하기 위한 런타임 기능 검색을 사용하는 한 확장 기능을 계속 사용할 수 있고, 이러한 확장 기능을 지원하지 않는 기기에 대해서는 대체 기능을 제공할 수 있습니다.
NDK 툴체인은 16바이트 스택 정렬 후에 함수를 호출하는 것으로 가정합니다. 기본 도구와 옵션은 이 규칙을 적용합니다. 어셈블리 코드를 작성할 경우 스택 정렬 상태를 유지하고 다른 컴파일러도 이 규칙을 준수하도록 해야 합니다.
자세한 내용은 다음 문서를 참조하세요.
- GCC 온라인 문서: Intel 386 및 AMD x86-64 옵션
- 다양한 C++ 컴파일러 및 운영체제의 호출 규칙
- Intel IA-32 Intel 아키텍처 소프트웨어 개발자 매뉴얼, 2권: 명령 집합 참조
- Intel IA-32 Intel 아키텍처 소프트웨어 개발자 매뉴얼, 3권: 시스템 프로그래밍 가이드
- 시스템 V 애플리케이션 바이너리 인터페이스: Intel386 프로세서 아키텍처 부록
x86_64
이 ABI는 보통 "x86-64"라고 하는 명령 집합을 지원하는 CPU를 위한 것으로, GCC가 일반적으로 다음 컴파일러 플래그로 생성하는 명령을 지원합니다.
-march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel
이러한 플래그는 MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2 및 POPCNT 명령 집합 확장과 함께, GCC 문서에 따라 x86-64 명령 집합을 대상으로 합니다. 생성되는 코드는 최고의 Intel 64비트 CPU 전반에 균형을 맞춰 최적화된 코드입니다.
(특히 성능 최적화와 관련된) 컴파일러 플래그에 대한 자세한 내용은 GCC x86 성능을 참조하세요.
이 ABI는 다음과 같이 다른 선택적 x86-64 명령 집합 확장은 포함하지 않습니다.
- MOVBE
- SHA
- AVX
- AVX2
이러한 확장 기능을 활성화하기 위한 런타임 기능 검색을 사용하는 한 확장 기능을 계속 사용할 수 있고, 이러한 확장 기능을 지원하지 않는 기기에 대해서는 대체 기능을 제공할 수 있습니다.
자세한 내용은 다음 문서를 참조하세요.
- 다양한 C++ 컴파일러 및 운영체제의 호출 규칙
- Intel64 및 IA-32 아키텍처 소프트웨어 개발자 매뉴얼, 2권:명령 집합 참조
- Intel64 및 IA-32 Intel 아키텍처 소프트웨어 개발자 매뉴얼, 3권: 시스템 프로그래밍
mips
이 ABI는 MIPS32r1 명령 집합 이상을 지원하는 MIPS 기반 CPU를 위한 것으로, 다음과 같은 특징이 있습니다.
- MIPS32 수정 버전 1 ISA
- 리틀 엔디언
- O32
- 하드웨어 부동 소수점
- DSP 애플리케이션 고유의 확장 기능 없음
자세한 내용은 다음 문서를 참조하세요.
- 프로그래머용 아키텍처("MIPSARCH")
- ELF 시스템 V 애플리케이션 바이너리 인터페이스
- Itanium/Generic C++ ABI
더 구체적인 세부정보는 MIPS32 아키텍처를 참조하세요. 일반적인 질문에 대한 대답은 MIPS FAQ에서 확인할 수 있습니다.
mips64
이 ABI는 MIPS64 R6을 위한 것입니다. 자세한 내용은 MIPS64 아키텍처를 참조하세요.
특정 ABI를 위한 코드 생성
기본적으로, NDK는 armeabi ABI용 기계어 코드를 생성합니다. 대신에 Application.mk
파일에 다음 줄을 추가하여 ARMv7-a와 호환되는 기계어 코드를 생성할 수 있습니다.
APP_ABI := armeabi-v7a
두 개 이상의 고유 ABI에 대한 기계어 코드를 빌드하려면 공백을 구분 기호로 사용하세요. 예:
APP_ABI := armeabi armeabi-v7a
이렇게 설정하면 NDK는 이 줄에 나열된 각 ABI마다 하나씩, 두 가지 버전의 기계어 코드를 빌드합니다. APP_ABI
변수에 대해 지정할 수 있는 값에 대한 추가 정보는 Android.mk를 참조하세요.
여러 기계어 코드 버전을 빌드할 때, 빌드 시스템은 애플리케이션 프로젝트 경로에 라이브러리를 복사하고 최종적으로는 APK로 라이브러리를 패키지로 묶어 팻 바이너리(fat binary)를 만듭니다. 팻 바이너리는 단일 시스템용 기계어 코드만 포함한 것보다는 크고 그 덕분에 호환성의 폭은 더 넓어지지만, APK가 커진다는 대가를 치러야 합니다.
설치 시, 패키지 관리자가 대상 기기에 가장 알맞은 기계어 코드만 압축을 풉니다. 자세한 내용은 설치 시 네이티브 코드의 자동 압축 풀기를 참조하세요.
Android 플랫폼에서의 ABI 관리
이 섹션에서는 Android 플랫폼이 APK에서 네이티브 코드를 관리하는 방법을 자세히 설명합니다.
앱 패키지의 네이티브 코드
Play Store와 Package Manager는 둘 다 다음 패턴과 일치하는 APK 내부의 파일 경로에서 NDK가 생성한 라이브러리를 찾을 수 있을 것으로 예상합니다.
/lib/<abi>/lib<name>.so
여기서 <abi>
는 지원되는 ABI에 나열되어 있는 ABI 이름 중 하나이고, <name>
은 Android.mk
파일에서 LOCAL_MODULE
변수에 대해 정의한 라이브러리의 이름입니다. APK 파일은 zip 파일일 뿐이므로, 파일을 열어서 공유된 네이티브 라이브러리가 속한 위치를 확인하는 작업은 별 것 아닙니다.
시스템이 예상한 위치에서 네이티브 공유 라이브러리를 찾지 못하면 이러한 라이브러리를 사용할 수 없습니다. 그럴 때는 앱 자체가 라이브러리를 복사한 다음 dlopen()
을 수행해야 합니다.
팻 바이너리에서는 각 라이브러리가 상응하는 ABI와 이름이 일치하는 디렉터리에 있습니다. 예를 들어, 팻 바이너리는 다음을 포함할 수 있습니다.
/lib/armeabi/libfoo.so /lib/armeabi-v7a/libfoo.so /lib/arm64-v8a/libfoo.so /lib/x86/libfoo.so /lib/x86_64/libfoo.so /lib/mips/libfoo.so /lib/mips64/libfoo.so
참고: 4.0.3 또는 이전 버전을 실행하는 ARMv7 기반 Android 기기는 armeabi
디렉터리와 armeabi-v7a
디렉터리가 둘 다 있으면 후자 대신 전자에서 네이티브 라이브러리를 설치합니다. 이는 /lib/armeabi/
가 APK의 /lib/armeabi-v7a/
뒤에 오기 때문입니다. 4.0.4 버전부터는 이 문제가 수정되었습니다.
Android 플랫폼 ABI 지원
빌드 고유의 시스템 속성이 다음을 표시하기 때문에, Android 시스템은 런타임에 어떤 ABI를 지원하는지 알게 됩니다.
- 시스템 이미지 자체에 사용되는 기계어 코드에 해당하는, 기기에 대한 기본 ABI.
- 시스템 이미지도 지원하는 다른 ABI에 해당하는, 선택적인 보조 ABI.
이 메커니즘은 시스템이 설치 시 패키지로부터 최상의 기계어 코드를 추출하도록 합니다.
최상의 성능을 위해서는 기본 ABI에 대해 직접 컴파일해야 합니다. 예를 들어, 전형적인 ARMv5TE 기반의 기기는 기본 ABI인 armeabi
만 정의합니다. 반면에, 전형적인 ARMv7 기반의 기기는 기본 ABI를 armeabi-v7a
로, 보조 ABI를 armeabi
로 정의하는데, 이들 각각에 대해 생성되는 애플리케이션 네이티브 바이너리를 실행할 수 있기 때문입니다.
많은 x86 기반 기기는 armeabi-v7a
및 armeabi
NDK 바이너리를 실행할 수도 있습니다. 이러한 기기의 경우 기본 ABI는 x86
, 보조 ABI는 armeabi-v7a
가 됩니다.
전형적인 MIPS 기반 기기는 기본 ABI인 mips
만 정의합니다.
설치 시 네이티브 코드의 자동 압축 풀기
애플리케이션을 설치할 때, 패키지 관리자 서비스는 APK를 검사하여 다음 양식의 공유 라이브러리가 있는지 찾아봅니다.
lib/<primary-abi>/lib<name>.so
공유 라이브러리가 하나도 발견되지 않고 보조 ABI를 정의한 경우 이 서비스는 다음 양식의 공유 라이브러리가 있는지 검사합니다.
lib/<secondary-abi>/lib<name>.so
위 양식의 라이브러리가 발견되면 패키지 관리자는 이를 애플리케이션의 data
디렉터리(data/data/<package_name>/lib/
)에 있는 /lib/lib<name>.so
로 복사합니다.
공유 객체 파일이 하나도 없을 경우 애플리케이션이 빌드되고 설치되지만 런타임에 다운됩니다.