일반적인 문제 및 해결 방법

이 문서는 버그는 아니지만 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를 사용합니다. mmap64android-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++ ABItype_info 포인터가 같은 경우에만 두 객체의 유형이 동일하다고 명시합니다. catch 구문의 type_info가 발생한 예외와 일치하는 경우에만 예외가 감지될 수 있습니다. 동일한 규칙이 dynamic_cast에도 적용됩니다.

유형에 키 함수가 없으면 typeinfo가 약한 기호로 방출되고 라이브러리가 로드될 때 일치하는 유형 정보가 병합됩니다. 실행 파일이 로드된 후 동적으로 라이브러리를 로드하면(즉, dlopen 또는 System.loadLibrary를 통함) 로더가 로드된 라이브러리의 유형 정보를 병합하지 못할 수도 있습니다. 이런 경우 두 유형은 동일한 것으로 간주되지 않습니다.

미리 빌드된 불일치 라이브러리 사용

애플리케이션에 미리 빌드된 라이브러리(일반적으로 타사 라이브러리)를 사용하는 경우 더 주의해야 합니다. 일반적으로 다음 규칙을 알아야 합니다.

  • 결과적으로 앱의 최소 API 수준은 모든 앱 라이브러리의 최대 minSdkVersion입니다.

    minSdkVersion은 16이지만, 21용으로 미리 빌드된 라이브러리를 사용하고 있다면 앱의 최소 API 수준은 21이 됩니다. 이 사항을 준수하지 않으면 미리 빌드된 정적 라이브러리는 빌드 시간에 표시되지 않지만, 미리 빌드된 공유 라이브러리는 실행 시간까지 표시되지 않을 수도 있습니다.

  • 모든 라이브러리는 동일한 NDK 버전으로 생성되어야 합니다.

    이 규칙은 중단이 드물게 발생하므로 더 유연하지만, 서로 다른 NDK 주요 버전으로 빌드된 라이브러리 간의 호환성을 보장하지 않습니다. C++ ABI는 안정적이지 않으며 이전에 변경되었습니다.

  • 여러 개의 공유 라이브러리가 있는 앱은 공유 STL을 사용해야 합니다.

    일치하지 않는 STL을 사용할 때 발생하는 문제는 많은 주의를 기울여 피할 수 있지만, 이 문제 자체를 방지하는 것이 더 좋습니다. 문제를 방지하는 가장 좋은 방법은 앱에 여러 개의 공유 라이브러리를 사용하지 않는 것입니다.