2011-06-23 12 views
11

Tengo algunos problemas al usar la API de carga dinámica (<dlfcn.h>: dlopen(), dlclose(), etc.) en Android. Estoy usando la cadena de herramientas independiente NDK (versión 8) para compilar las aplicaciones y bibliotecas. La versión de Android es 2.2.1 Froyo.Error de segmentación al utilizar dlclose (...) en la plataforma Android

Aquí está el código fuente de la biblioteca compartida simple.

#include <stdio.h> 

int iii = 0; 
int *ptr = NULL; 

__attribute__((constructor)) 
static void init() 
{ 
    iii = 653; 
} 

__attribute__((destructor)) 
static void cleanup() 
{ 
} 

int aaa(int i) 
{ 
    printf("aaa %d\n", iii); 
} 

Aquí está el código fuente del programa que utiliza la biblioteca mencionada.

#include <dlfcn.h> 
#include <stdlib.h> 
#include <stdio.h> 

int main() 
{ 
    void *handle; 
    typedef int (*func)(int); 
    func bbb; 

    printf("start...\n"); 

    handle = dlopen("/data/testt/test.so", RTLD_LAZY); 
    if (!handle) 
    { 
     return 0; 
    } 

    bbb = (func)dlsym(handle, "aaa"); 
    if (bbb == NULL) 
    { 
     return 0; 
    } 

    bbb(1); 

    dlclose(handle); 
    printf("exit...\n"); 

    return 0; 
} 

Con estas fuentes todo está funcionando bien, pero cuando trato de utilizar algunas funciones STL o clases, el programa se bloquea con un error de segmentación, cuando los main() sale de la función, por ejemplo, cuando se utiliza esta código fuente para la biblioteca compartida.

#include <iostream> 

using namespace std; 

int iii = 0; 
int *ptr = NULL; 

__attribute__((constructor)) 
static void init() 
{ 
    iii = 653; 
} 

__attribute__((destructor)) 
static void cleanup() 
{ 
} 

int aaa(int i) 
{ 
    cout << iii << endl; 
} 

Con este código, el programa se bloquea con el fallo de segmentación después o durante la salida de la función main(). He intentado un par de pruebas y encontré los siguientes resultados.

  1. Sin usar STL todo está funcionando bien.
  2. Cuando use STL y no llame al dlclose() al final, todo está funcionando bien.
  3. He intentado compilar con varios indicadores de compilación como -fno-use-cxa-atexit o -fuse-cxa-atexit, el resultado es el mismo.

¿Qué hay de malo en mi código que usa el STL?

+0

+1 Buena pregunta formateada;) –

+0

¿El encabezado STL está en el encabezado de ese archivo? ¿Podría llevarlo solo al archivo cpp? (Entonces STL no estará en la interfaz.) ¿Están la definición y la declaración separadas? – Naszta

+0

Supongo que está hablando de la función aaa (...), si es así, entonces la declaración y la definición están en archivos diferentes. El archivo de encabezado de definición es '#ifdef __cplusplus extern" C " #endif int aaa (int i);' –

Respuesta

-2

Debe utilizar extern "C" para declarar a funcionar aaa()

+1

Se hace en el archivo de encabezado apropiado, no agregué el contenido de ese archivo en la pregunta. –

-1

Se necesita compilar con -fpic como un indicador del compilador para la aplicación que está usando dlopen() y dlclose(). También debe intentar el manejo de errores a través del dlerror() y quizás verifique si la asignación de su puntero de función es válida, incluso si no es NULL el puntero de la función podría estar apuntando a algo inválido desde la inicialización, no se garantiza que el dlsym() devuelva NULL en Android si no puede encontrar un simbolo Consulte la documentación de Android que se opone a las cosas que cumplen con posix, no todo es compatible con posix en Android.

0

Tengo una aversión general a llamar al dlclose(). El problema es que debe asegurarse de que nada intente ejecutar código en la biblioteca compartida después de que se haya desasignado, o obtendrá un error de segmentación.

La forma más común de error es crear un objeto cuyo destructor está definido en el código de llamadas definido en la biblioteca compartida. Si el objeto aún existe después de dlclose(), su aplicación se bloqueará cuando se elimine el objeto.

Si observa el logcat, debería ver un rastro de la pila depurada. Si puedes decodificar eso con la herramienta arm-eabi-addr2line, deberías poder determinar si está en un destructor, y si es así, para qué clase.Alternativamente, tome la dirección de bloqueo, elimine los 12 bits altos y utilícela como un desplazamiento en la biblioteca que fue dlclose() d e intente descubrir qué código reside en esa dirección.

7

Parece que encontré el motivo del error. He tratado de otro ejemplo con los siguientes archivos de origen: Aquí está el código fuente de la clase simple: MyClass.h

class MyClass 
{ 
public: 
    MyClass(); 
    ~MyClass(); 
    void Set(); 
    void Show(); 
private: 
    int *pArray; 
}; 

MyClass.cpp

#include <stdio.h> 
#include <stdlib.h> 
#include "myclass.h" 

MyClass::MyClass() 
{ 
    pArray = (int *)malloc(sizeof(int) * 5); 
} 

MyClass::~MyClass() 
{ 
    free(pArray); 
    pArray = NULL; 
} 

void MyClass::Set() 
{ 
    if (pArray != NULL) 
    { 
     pArray[0] = 0; 
     pArray[1] = 1; 
     pArray[2] = 2; 
     pArray[3] = 3; 
     pArray[4] = 4; 
    } 
} 

void MyClass::Show() 
{ 
    if (pArray != NULL) 
    { 
     for (int i = 0; i < 5; i++) 
     { 
      printf("pArray[%d] = %d\n", i, pArray[i]); 
     } 
    } 
} 

Como se puede ver en el código No usé ninguna cosa relacionada con STL. Aquí están los archivos fuente de las exportaciones de la biblioteca de funciones. func.h

#ifdef __cplusplus 
extern "C" { 
#endif 

int SetBabe(int); 
int ShowBabe(int); 

#ifdef __cplusplus 
} 
#endif 

func.cpp

#include <stdio.h> 
#include "myclass.h" 
#include "func.h" 

MyClass cls; 

__attribute__((constructor)) 
static void init() 
{ 

} 

__attribute__((destructor)) 
static void cleanup() 
{ 

} 

int SetBabe(int i) 
{ 
    cls.Set(); 
    return i; 
} 

int ShowBabe(int i) 
{ 
    cls.Show(); 
    return i; 
} 

Y finalmente este es el código fuente de la Programm que utiliza la biblioteca. main.cpp

#include <dlfcn.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include "../simple_lib/func.h" 

int main() 
{ 
    void *handle; 
    typedef int (*func)(int); 
    func bbb; 

    printf("start...\n"); 

    handle = dlopen("/data/testt/test.so", RTLD_LAZY); 
    if (!handle) 
    { 
     printf("%s\n", dlerror()); 
     return 0; 
    } 

    bbb = (func)dlsym(handle, "SetBabe"); 
    if (bbb == NULL) 
    { 
     printf("%s\n", dlerror()); 
     return 0; 
    } 
    bbb(1); 

    bbb = (func)dlsym(handle, "ShowBabe"); 
    if (bbb == NULL) 
    { 
     printf("%s\n", dlerror()); 
     return 0; 
    } 
    bbb(1); 

    dlclose(handle); 
    printf("exit...\n"); 

    return 0; 
} 

De nuevo, como se puede ver el programa usando la biblioteca también no utilizar ningún material relacionado STL, pero después de ejecución del programa que tiene el mismo fallo de segmentación durante main(...) salida de la función. Por lo tanto, el problema no está relacionado con STL en sí y está escondido en otro lugar. Luego, después de una larga investigación, encontré el error. Normalmente, las variables estáticas C++ destructors se llaman inmediatamente antes de la salida de la función main(...), si están definidas en el programa principal, o si están definidas en alguna biblioteca y usted lo está utilizando, los destructores deben llamarse inmediatamente antes de dlclose(...). En el sistema operativo Android todos los destructores (definidos en el programa principal o en alguna biblioteca que esté utilizando) de variables estáticas de C++ se invocan durante la salida de la función main(...). Entonces, ¿qué pasa en nuestro caso? Tenemos cls variable C++ estática definida en la biblioteca que estamos usando. Luego, inmediatamente antes de la salida de la función main(...), llamamos a la función dlclose(...), como resultado, la biblioteca cerrada y cls deja de ser válida. Pero el puntero de cls se almacena en algún lugar y se debe invocar su destructor durante la salida de la función main(...), y porque en el momento de la llamada ya no es válido, obtenemos un error de segmentación. Entonces la solución es no llamar al dlclose(...) y todo debería estar bien. Desafortunadamente con esta solución no podemos usar atributo ((destructor)) para desinicializar algo que queremos desinicializar, porque se llama como resultado de la llamada dlclose(...).

+2

Ha habido numerosas correcciones para el manejo de destructor en la libc de Android a lo largo del tiempo, por lo que el comportamiento exacto probablemente dependerá de la versión específica de Android que esté utilizando. Recomiendo presentar un error en b.android.com, con el código de ejemplo, explicando qué comportamiento está viendo y qué está esperando. – fadden

0

Me encontré con el mismo dolor de cabeza en Linux. Una solución alternativa que soluciona mi violación de segmento es poner estas líneas en el mismo archivo que main(), por lo que dlclose() se llama después vuelve principales:

static void* handle = 0; 
void myDLClose(void) __attribute__ ((destructor)); 
void myDLClose(void) 
{ 
    dlclose(handle); 
} 

int main() 
{ 
    handle = dlopen(...); 
    /* ... real work ... */ 
    return 0; 
} 

La causa fundamental de la violación de segmento dlclose inducida puede haber que una implementación particular de dlclose() no limpia las variables globales dentro del objeto compartido.

Cuestiones relacionadas