Problemas com o JNI: undefined symbol: fp_init [RESOLVIDO]

16 respostas
O

Boa tarde ao pessoal do Guj. Eu estou trabalhando em cima de uma aplicação que usa JNI para acessar métodos nativos em C.
O código escrito em C trabalha em cima de uma biblioteca, a libfprint.
Pois bem, eu escrevi meu código C:

#include <stdio.h>
#include <stdlib.h>
#include <libfprint/fprint.h>
#include <string.h>
#include <jni.h>
#include "Leitor.h"

struct fp_dev* fpdev = NULL;
struct fp_dscv_dev **discovered_devs = NULL;
struct fp_print_data **printdata = NULL;

JNIEXPORT jint JNICALL
Java_Leitor_principal(JNIEnv *env, jobject obj) {
    //Variável usada por todos os resultados.
    int r;

    /* Inicia o libFprint() */

    r = fp_init();
    if (r < 0)
        return r;

//********** um monte de código *******

//Encerra minha aplicacao
fp_dev_close(fpdev);

Criei minha classe Java:

public class Leitor {
    private native int principal();
    public static void main(String[] args) {
        new Leitor().principal();
    }
    static {
        System.loadLibrary("Leitor");
    }
}

Depois trilheii minuciosamente os passos abaixo para compilação:

javac Leitor.java
javah -jni Leitor
gcc -Wall -fPIC -c Leitor.c -I/opt/jdk1.6.0_11/include/linux/ -I/opt/jdk1.6.0_11/include/ -I/usr/include/libfprint/ -lfprint
gcc -shared -Wl,-soname,libLeitor.so -o libLeitor.so Leitor.o
LD_LIBRARY_PATH=.
export LD_LIBRARY_PATH
java Leitor

Mas retorna a seguinte mensagem de erro:

undefined symbol: fp_init

Eu pude notar que o JNI não reconhece as funções da biblioteca libfprint, somente as bibliotecas padrões.
Eu fiz um teste: apaguei todos as funções do libfprint e deixei somente as funções padrões do C, tais como
printf, scanf, fflush, etc. Daí eu coloquei pra rodar com JNI e deu certo.
Daí fiz outro teste: rodei a aplicação em C puro, sem o JNI, mas com as funções do libfprint, ou seja, meu
programa completo. Mais uma vez funcionou.

Alguém pode me ajudar? Desde já agradeço a todos.

16 Respostas

L

O DL não está encontrando a libfprint, seja via o caminho padrão ou via LD_LIBRARY_PATH.

O

Loud, você sabe como eu posso resolver isso?

Porque quando eu configuro o PATH, eu apenas informo que a minha biblioteca nativa está no diretório atual.
Mas o libfprint está em outra. Tem como eu setar o PATH para dois lugares diferentes, ao mesmo tempo?
Se sim, como? Abraço.

T

http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html

Ou seja,

export LD_LIBRARY_PATH=dirA:dirB:dirC:dirD
O

Ainda não deu certo.
Tentei na ordem que aparece abaixo:

export LD_LIBRARY_PATH=.:/usr/include/libfprint/
java Leitor

Mas retorna o mesmo erro:

java: symbol lookup error: /home/odair/NetBeansProjects/Leitor/libLeitor.so: undefined symbol: fp_init

Alguém tem mais alguma idéia do que pode ser? Enquanto isso eu vou continuar travando minha batalha aqui.

T

/usr/include/libfprint contém o arquivo .so que deve ser carregado? Esse arquivo .so tem a devida permissão de execução?

E a propósito, não termine os nomes de diretórios no LD_LIBRARY_PATH (exceto o diretório raiz) por “/”; não sei se o Linux se confunde com tais nomes de diretórios.

T

Continue a ler o arquivo http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html que lhe passei, e procure o item LD_DEBUG. Acho que o que ele recomenda fazer vai lhe ajudar bastante porque você vai ver o que o Linux está tentando fazer para achar o fp_init.

O

Até agora nada… :evil:

Agora está mais por tentaiva e erro mesmo.
Caso alguém tenha alguma idéia, pode dizer.

O

Bom, larguei a mão do JNI e parti pro JNA. Li alguma coisa sobre lá no blog do Urubatan e depois no java.net e achei mais simples.
Minha única dúvida é: como gerar o arquivo .h? Por acaso eu posso usar o javah -jni para fazer isso e depois continuar a usar
o jna ou tenho que criar essa biblioteca na mão?

Abaixo estão minhas duas classes:

/**
 * Esta é minha classe principal
 * @author odair
 */
public class LeitorRun {

    /**
     * meu método main
     */
    public static void main(String[] args) {
        LeitorLibrary lib = (LeitorLibrary) Native.loadLibrary("LeitorBiometrico", LeitorLibrary.class);
        lib.principal();

    }

}
import com.sun.jna.Library;
/**
* Esta é a minha interface
**/
public interface LeitorLibrary extends Library{

    public int principal();

}

O meu objetivo continua sendo o mesmo, que é rodar o método “principal” do
meu arquivo em C. LeitorBiometico deve ser o meu arquivo.h

T

No caso do JNA você se baseia no arquivo .h do cara que escreveu a DLL ou .SO original. Há no site do JNA uma tabela de correspondências (do tipo long do C é int do Java, mas long long do C é long do Java). Você não precisa de nada do seu código JNI original.

O

Thingol, isso significa que seu eu escrever um arquivo em C, eu devo gerar o .h dele?

T

Boa pergunta. Você tem um determinado .so que tem um método

int fp_init ();

Isso provavelmente está especificado em um arquivo .h, ou então está na sua documentação.

O JNA não requer a presença física de um arquivo .h ou .c ou sei lá o quê, mas exige que você saiba exatamente o que está fazendo, e que você tenha lido direito a sua documentação.

O

Saquei. Mas daí então eu não vou precisar gerar esse .so, pois “fp_init()” está dentro da função “principal()”, e o arquivo que contém os dois já informa o caminho do fprint nos includes. Quando eu for gerar um arquivo com a extensão .o, com o gcc, basta eu informar o caminho do arquivo que contém fp_init().

Até agora eu fiz o seguinte. Tenho meus arquivos em C, inclusive os .so, gerados e gravados em um diretório. Daí eu criei as minhas duas classes em JNA, como citado anteriormente. Daí quando eu vou rodar a aplicação pelo NetBeans aparece a seguinte mensagem:

Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'LeitorBiometrico': libLeitorBiometrico.so: cannot open shared object file: No such file or directory
        at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:155)
        at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:216)
        at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:191)
        at com.sun.jna.Library$Handler.<init>(Library.java:131)
        at com.sun.jna.Native.loadLibrary(Native.java:279)
        at com.sun.jna.Native.loadLibrary(Native.java:265)
        at br.com.jcomputacao.jcleitor.JCLeitorRun.main(JCLeitorRun.java:19)

“No JNI eu resolvia isso setando o PATH”, pensei. Então abri o terminal do linux, fui até NetBeansProjects/LeitorBiometrico/src e digitei:

export LD_LIBRARY_PATH=/home/odair..../LeitorBiometrico

Mas o danado do erro persistiu.
Daí pensei assim: “bom, talvez tenha que copiar todos fontes (incluindo .o, .so e compania) da pasta do LeitorBiometrico para pasta src do meu projeto JAVA”. Fiz isso novamente, mas não obtive êxito (pela enésima vez).

Alguém tem alguma idéia do que pode estar ocorrendo?

O

Dando uma olhada na documentação encontrei o método “addSearchPath”, que resolveu o problema de busca no diretório.
Veja o código abaixo:

public class LeitorRun {

    /**
     * @param args the command line arguments
     */
public static void main(String[] args) {
 
/**
* Aqui está o método
**/       
NativeLibrary.addSearchPath("LeitorBiometrico", "/home/odair/NetBeansProjects/LeitorBiometrico");
        
LeitorLibrary lib = (LeitorLibrary) Native.loadLibrary("LeitorBiometrico", LeitorLibrary.class);
        
lib.principal();

    }

}

Mas daí, quando vou rodá-lo no NetBeans, sabe qual o erro que aperece?

symbol lookup error: /home/odair/NetBeansProjects/LeitorBiometrico/libLeitorBiometrico.so: undefined symbol: fp_init

Pois é, esse cidadão voltou. Vou ver o que posso fazer e trago a solução mais tarde.

T
libLeitorBiometrico.so

Aham - será que o símbolo exportado é “fp_init” ou “_fp_init”? Dê uma olhada na lista de símbolos exportados por esse .so. Dica

man nm

O

Bom, olha eu aqui de novo.

Thingol, eu dei uma olhada no comando nm e depois dei um nm libLeitorBiometrico.so. Daí eu percebi que todas as funções do libfprint estão marcadas com “U”, que no man está como “The symbol is undefined”.
Em relação ao fp_init, ele aparece assim mesmo. Até pensei que o problema seja na hora de gerar a biblioteca em C, que ela não encontra os métodos do libfprint. Mas por outro lado, se o gcc não encontra, porque
me permite gerar minha biblioteca libLeitorBiometrico.so?

O

Bom, depois de alguns dias e muita leitura sem noção do manual do gcc em inglês, eu consegui fazer a minha aplicação rodar.

O meu erro era que quando eu gerava uma biblioteca (.so) eu a linkava somente com o meu arquivo em C, como você pode ver
abaixo:

gcc -shared -W1,-soname,libLeitorBiometrico.so -o libLeitorBiometrico.so LeitorBiometrico.o

Daí faltava eu linkar com o header também, o fprint.h. Daí era só acrescentar o parâmetro na hora de linkar as bibliotecas.

gcc -shared -W1,-soname,libLeitorBiometrico.so -o libLeitorBiometrico.so LeitorBiometrico.o  -lfprint

E daí funcionou!!! Esta foi uma solução tanto para o JNA quanto para o JNI.

Resumindo, todo o processo, desde a compilação até a linkagem, devem ficar assim:

gcc -fPIC -c LeitorBiometrico.c -w 
gcc -shared -W1,-soname,libLeitorBiometrico.so -o libLeitorBiometrico.so LeitorBiometrico.o  -lfprint

Após ler o manual do gcc, vi que eu não precisava do -wall, e que pra terminar de suprimir aquele warnings chatos
do terminal do linux bastava eu passar o -w no final da compilação. O que fica de experiência minha nessa saga é:

  • Leia os manuais, sejam eles em inglês, chinês ou tangamandapiano. Você não irá se arrepender e de quebra vai
    aprender um monte de coisas novas e legais. Obrigado a todos que me ajudaram.
Criado 9 de junho de 2009
Ultima resposta 16 de jun. de 2009
Respostas 16
Participantes 3