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
minSdkVersion
s 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.