이 문서는 버그는 아니지만 NDK를 사용할 때 발생하는 가장 일반적인 문제와 해결 방법의 일부 목록을 담고 있습니다.
이전 API 수준에서 _FILE_OFFSET_BITS=64
사용
통합 헤더 이전에는 NDK에서 _FILE_OFFSET_BITS=64
를 지원하지 않았습니다. 앱을 빌드할 때 이 옵션을 정의했다면 자동으로 무시됩니다. _FILE_OFFSET_BITS=64
옵션은 현재 통합 헤더에서 지원되지만, 이전 버전의 Android에서는 off_t
API의 극히 일부만 off64_t
변형으로 사용할 수 있었습니다. 따라서, 이전 API 수준에서 이 기능을 사용하면 사용 가능한 함수가 줄어들 수 있습니다.
이 문제는 r16 블로그 게시물과 Bionic 문서에서 자세히 설명하고 있습니다.
문제: 빌드에서 minSdkVersion
에 없는 API를 요청합니다.
해결 방법: _FILE_OFFSET_BITS=64
를 사용 중지하거나 minSdkVersion
을 올립니다.
mmap
정의가 선언되지 않았거나 암시적 정의임
C++에서 다음 오류가 표시될 수 있습니다.
error: use of undeclared identifier 'mmap'
또는 C에서 다음 오류가 발생할 수 있습니다.
warning: implicit declaration of function 'mmap' is invalid in C99
_FILE_OFFSET_BITS=64
를 사용하면 C 라이브러리에서 mmap
대신 mmap64
를 사용합니다. mmap64
는 android-21
까지 사용할 수 없었습니다. minSdkVersion
값이 21보다 낮은 경우 C 라이브러리는 _FILE_OFFSET_BITS=64
와 호환되는 mmap
을 포함하지 않으므로 함수를 사용할 수 없습니다.
minSdkVersion
이 기기 API 수준보다 높게 설정됨
NDK로 빌드하는 API 수준은 자바의 compileSdkVersion
과 의미가 매우 다릅니다. NDK API 수준은 앱에서 지원하는 최소 API 수준입니다. ndk-build에서 이는 APP_PLATFORM
설정입니다. CMake에서는 -DANDROID_PLATFORM
입니다.
함수에 관한 참조는 라이브러리가 처음 호출될 때가 아닌 로드될 때 결정되므로, 항상 표시되는 API가 아니면 참조할 수 없고 API 수준 검사로 API 사용을 보호할 수 없습니다. API가 참조되는 경우가 있다면 표시되어야 합니다.
문제: NDK API 수준이 기기에서 지원하는 API 수준보다 높습니다.
해결 방법: NDK API 수준(APP_PLATFORM
)을 앱에서 지원하는 최소 Android 버전으로 설정합니다.
빌드 시스템 | 설정 |
---|---|
ndk-build | APP_PLATFORM |
CMake | ANDROID_PLATFORM |
externalNativeBuild | android.minSdkVersion |
다른 빌드 시스템의 경우 다른 빌드 시스템에서 NDK 사용을 참고하세요.
__aeabi
기호를 찾을 수 없음
다음 메시지가 표시됩니다.
UnsatisfiedLinkError: dlopen failed: cannot locate symbol "
__aeabi_memcpy
"
이는 발생할 수 있는 런타임 오류의 한 가지 예입니다. 네이티브 라이브러리를 로드하려고 시도할 때 이러한 오류가 로그에 표시됩니다. 기호는 __aeabi_*
중 하나일 수 있습니다. __aeabi_memcpy
및 __aeabi_memclr
이 가장 일반적입니다.
이 문제는 문제 126에 명시되어 있습니다.
rand
기호를 찾을 수 없음
오류 로그 메시지는 다음과 같습니다.
UnsatisfiedLinkError: dlopen failed: cannot locate symbol "
rand
"
자세한 내용은 Stack Overflow 답변을 참고하세요.
정의되지 않은 __atomic_*
참조
문제: 일부 ABI에서는 원자적 연산의 일부를 구현하기 위해 libatomic
이 필요합니다.
해결 방법: 연결 시 -latomic
을 추가합니다.
오류 메시지는 다음과 같습니다.
error: undefined reference to '
__atomic_exchange_4
'
여기서 실제 기호에는 __atomic_
이라는 접두어가 포함될 수 있습니다.
라이브러리 경계에서 RTTI/예외가 작동하지 않음
문제: 공유 라이브러리 경계에서 발생한 예외가 포착되지 않거나 dynamic_cast
가 실패합니다.
해결 방법: 유형에 키 함수를 추가합니다. 키 함수는 유형의 인라인이 아닌 첫 번째 비순수 가상 함수입니다. 이에 관한 예는 문제 533의 설명을 참고하세요.
C++ ABI는 type_info
포인터가 같은 경우에만 두 객체의 유형이 동일하다고 명시합니다. catch 구문의 type_info
가 발생한 예외와 일치하는 경우에만 예외가 감지될 수 있습니다. 동일한 규칙이 dynamic_cast
에도 적용됩니다.
유형에 키 함수가 없으면 typeinfo
가 약한 기호로 방출되고 라이브러리가 로드될 때 일치하는 유형 정보가 병합됩니다. 실행 파일이 로드된 후 동적으로 라이브러리를 로드하면(즉, dlopen
또는 System.loadLibrary
를 통함) 로더가 로드된 라이브러리의 유형 정보를 병합하지 못할 수도 있습니다. 이런 경우 두 유형은 동일한 것으로 간주되지 않습니다.
미리 빌드된 불일치 라이브러리 사용
애플리케이션에 미리 빌드된 라이브러리(일반적으로 타사 라이브러리)를 사용하는 경우 더 주의해야 합니다. 일반적으로 다음 규칙을 알아야 합니다.
결과적으로 앱의 최소 API 수준은 모든 앱 라이브러리의 최대
minSdkVersion
입니다.minSdkVersion
은 16이지만, 21용으로 미리 빌드된 라이브러리를 사용하고 있다면 앱의 최소 API 수준은 21이 됩니다. 이 사항을 준수하지 않으면 미리 빌드된 정적 라이브러리는 빌드 시간에 표시되지 않지만, 미리 빌드된 공유 라이브러리는 실행 시간까지 표시되지 않을 수도 있습니다.모든 라이브러리는 동일한 NDK 버전으로 생성되어야 합니다.
이 규칙은 중단이 드물게 발생하므로 더 유연하지만, 서로 다른 NDK 주요 버전으로 빌드된 라이브러리 간의 호환성을 보장하지 않습니다. C++ ABI는 안정적이지 않으며 이전에 변경되었습니다.
여러 개의 공유 라이브러리가 있는 앱은 공유 STL을 사용해야 합니다.
일치하지 않는 STL을 사용할 때 발생하는 문제는 많은 주의를 기울여 피할 수 있지만, 이 문제 자체를 방지하는 것이 더 좋습니다. 문제를 방지하는 가장 좋은 방법은 앱에 여러 개의 공유 라이브러리를 사용하지 않는 것입니다.