Problemas comuns e soluções

Este documento é uma lista parcial dos não-erros mais encontrados que podem ser encontrados ao usar o NDK e as soluções correspondentes, se disponíveis.

Como usar _FILE_OFFSET_BITS=64 com níveis de API mais antigos

Antes dos cabeçalhos unificados, o NDK não era compatível com _FILE_OFFSET_BITS=64. Se você o definiu ao criar seu app, ele foi ignorado silenciosamente. A opção _FILE_OFFSET_BITS=64 agora é compatível com cabeçalhos unificados, mas em versões anteriores do Android, poucas APIs off_t estavam disponíveis como uma variante off64_t. Portanto, o uso desse recurso com níveis de API antigos resulta em menos funções disponíveis.

Esse problema é explicado em detalhes na postagem do blog do r16 e na documentação do Bionic.

Problema: sua versão está solicitando APIs que não existem na sua minSdkVersion.

Solução: desative _FILE_OFFSET_BITS=64 ou aumente a minSdkVersion.

Definição não declarada ou implícita de mmap

É possível encontrar o seguinte erro em C++:

erro: uso não declarado de identificador 'mmap'

ou o seguinte erro em C:

aviso: a declaração implícita da função 'mmap' é inválida em C99

O uso de _FILE_OFFSET_BITS=64 instrui a biblioteca C a usar mmap64 em vez de mmap. mmap64 estava indisponível até android-21. Se o valor de minSdkVersion for menor que 21, a biblioteca C não conterá um mmap compatível com _FILE_OFFSET_BITS=64. Por isso, a função não estará disponível.

O minSdkVersion foi definido como superior ao nível da API do dispositivo

O nível da API usado no NDK tem um significado muito diferente do compileSdkVersion para Java. O nível da API do NDK é o nível mínimo compatível com o app. No ndk-build, essa é a configuração do APP_PLATFORM. Com o CMake, ele é -DANDROID_PLATFORM.

Como as referências a funções normalmente são resolvidas quando as bibliotecas são carregadas, e não quando são chamadas pela primeira vez, não é possível referir-se a APIs que nem sempre estão presentes e proteger o uso delas com verificações de nível de API. Caso sejam mencionados, eles precisam estar presentes.

Problema: o nível da API do NDK é maior do que a API compatível com o dispositivo.

Solução: defina o nível da API do NDK (APP_PLATFORM) para a versão mínima do Android compatível com o app.

Sistema de compilação Configuração
ndk-build APP_PLATFORM
CMake ANDROID_PLATFORM
externalNativeBuild android.minSdkVersion

Para outros sistemas de compilação, consulte Usar o NDK com outros sistemas de compilação.

Não foi possível localizar símbolos __aeabi

A seguinte mensagem:

UnsatisfiedLinkError: dlopen failed: não é possível localizar o símbolo "__aeabi_memcpy"

é um exemplo de possíveis erros de tempo de execução. Esses erros aparecem no registro quando você tenta carregar suas bibliotecas nativas. O símbolo pode ser qualquer __aeabi_*; __aeabi_memcpy e __aeabi_memclr parecem ser os mais comuns.

Este problema está documentado no Problema 126

Não foi possível localizar o símbolo rand

Para a seguinte mensagem de registro de erro:

UnsatisfiedLinkError: dlopen falhou: não é possível localizar o símbolo "rand"

Veja esta resposta detalhada do Stack Overflow (link em inglês).

Referência não definida para __atomic_*

Problema: algumas ABIs precisam de libatomic para fornecer algumas implementações para operações atômicas.

Solução: adicione -latomic ao vincular.

Para a seguinte mensagem de erro:

erro: referência indefinida para "__atomic_exchange_4"

o símbolo real aqui pode ser qualquer coisa prefixada com __atomic_.

A RTTI/exceção não funciona entre os limites da biblioteca

Problema: as exceções não estão sendo capturadas quando acionadas em limites da biblioteca compartilhada ou dynamic_cast falha.

Solução: adicione uma função chave aos seus tipos. Uma função-chave é a primeira função virtual não puras, fora da linha, para um tipo. Por exemplo, consulte a discussão em Problema 533 (link em inglês).

A ABI C++ afirma que dois objetos têm o mesmo tipo apenas se os ponteiros type_info forem idênticos. As exceções só poderão ser capturadas se type_info para a captura corresponder à exceção lançada. A mesma regra se aplica a dynamic_cast.

Quando um tipo não tem uma função de chave, o typeinfo é emitido como um símbolo fraco e as informações do tipo correspondente são mescladas quando as bibliotecas são carregadas. Ao carregar bibliotecas dinamicamente após o executável ser carregado (em outras palavras, via dlopen ou System.loadLibrary), o carregador não pode mesclar informações de tipo das bibliotecas carregadas. Quando isso acontece, os dois tipos não são considerados iguais.

Como usar bibliotecas pré-compiladas incompatíveis

O uso de bibliotecas pré-compiladas. Normalmente, elas são bibliotecas de terceiros, e o aplicativo requer um pouco mais de cuidado. Em geral, esteja ciente das seguintes regras:

  • O nível mínimo de API do app resultante é o máximo de minSdkVersions de todas as bibliotecas.

    Se sua minSdkVersion for 16, mas você estiver usando uma biblioteca pré-criada em 21, o nível mínimo de API do app resultante será 21. Será possível ver se isso não foi seguido momento da compilação se a biblioteca pré-compilada for estática, mas poderá não aparecer até o tempo de execução para bibliotecas compartilhadas pré-compiladas.

  • Todas as bibliotecas precisam ser geradas com a mesma versão do NDK.

    Essa regra é um pouco mais flexível do que a maioria porque as quebras são raras, mas a compatibilidade entre bibliotecas criadas com diferentes versões principais do NDK não é garantida. A ABI C++ não é estável e foi modificada no passado.

  • Apps com várias bibliotecas compartilhadas precisam usar uma STL compartilhada.

    Como acontece com STLs incompatíveis, os problemas causados por isso podem ser evitados com muito cuidado, mas o melhor é evitar o problema. A melhor maneira de evitar esse problema é evitar ter várias bibliotecas compartilhadas no seu app.