Ошибка сегментации при использовании dlclose (...) на платформе Android - PullRequest
11 голосов
/ 23 июня 2011

У меня есть некоторые проблемы при использовании API динамической загрузки (<dlfcn.h>: dlopen(), dlclose() и т. Д.) На Android. Я использую автономный инструментарий NDK (версия 8) для компиляции приложений и библиотек. Версия Android 2.2.1 Froyo.

Вот исходный код простой общей библиотеки.

#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);
}

Вот исходный код программы, который использует указанную библиотеку.

#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;
}

С этими источниками все работает нормально, но , когда я пытаюсь использовать некоторые функции или классы STL, программа падает с ошибкой сегментации , когда выходит из функции main() Например, при использовании этого исходного кода для общей библиотеки.

#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;
}

С этим кодом программа завершается с ошибкой сегментации после или во время выхода из функции main(). Я попробовал пару тестов и нашел следующие результаты.

  1. Без использования STL все работает нормально.
  2. Когда используется STL и не вызывается dlclose() в конце, все работает нормально.
  3. Я пытался скомпилировать с различными флагами компиляции, такими как -fno-use-cxa-atexit или -fuse-cxa-atexit, результат тот же.

Что не так в моем коде, который использует STL?

Ответы [ 5 ]

7 голосов
/ 01 июля 2011

Похоже, я нашел причину ошибки. Я пробовал другой пример со следующими исходными файлами: Вот исходный код простого класса: 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]);
        }
    }
}

Как видно из кода, я не использовал ничего, связанного с STL. Вот исходные файлы экспорта библиотеки функций. 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;
}

И, наконец, это исходный код программы, которая использует библиотеку. 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;
}

Опять же, как вы можете видеть, программа, использующая библиотеку, также не использует ничего, связанного с STL, но после запуска программы я получил ту же ошибку сегментации при выходе из функции main(...). Таким образом, проблема не связана с самой STL, и она скрыта в каком-то другом месте. Затем, после долгих исследований, я нашел ошибку. Обычно destructors статических переменных C ++ вызывается непосредственно перед выходом из функции main(...), если они определены в основной программе или если они определены в некоторой библиотеке, и вы используете ее, то деструкторы должны вызываться непосредственно перед dlclose(...). В ОС Android все деструкторы (определенные в основной программе или в используемой вами библиотеке) статических переменных C ++ вызываются при выходе из функции main(...). Так что же происходит в нашем случае? У нас есть cls статическая переменная C ++, определенная в используемой нами библиотеке. Затем непосредственно перед выходом из функции main(...) мы вызываем функцию dlclose(...), в результате библиотека результатов закрывается и cls становится недействительной. Но указатель cls хранится где-то, и его деструктор должен вызываться во время выхода из функции main(...), а поскольку во время вызова он уже недействителен, мы получаем ошибку сегментации. Поэтому решение состоит в том, чтобы не звонить dlclose(...), и все должно быть в порядке. К сожалению, с этим решением мы не можем использовать атрибут ((деструктор)) для деинициализации того, что мы хотим деинициализировать, потому что оно вызывается в результате вызова dlclose(...).

0 голосов
/ 25 августа 2017

Я столкнулся с той же головной болью в Linux. Обходной путь, который исправляет мой segfault, - поместить эти строки в тот же файл, что и main (), так что dlclose () вызывается после main return:

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

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

Основная причина segfault, вызванного dlclose, может заключаться в том, что конкретная реализация dlclose () не очищает глобальные переменные внутри общего объекта.

0 голосов
/ 27 июня 2011

Вам необходимо скомпилировать с -fpic в качестве флага компилятора для приложения, использующего dlopen() и dlclose().Вам также следует попробовать обработать ошибки через dlerror() и, возможно, проверить, является ли присвоение указателя вашей функции действительным, даже если он не равен NULL, указатель функции может указывать на что-то недопустимое при инициализации, dlsym() не гарантирует возврата NULLна Android, если он не может найти символ.Обратитесь к документации по Android, в отличие от материалов, совместимых с posix, но не все являются posix-совместимыми на Android.

0 голосов
/ 01 июля 2011

У меня есть общее отвращение к вызову dlclose(). Проблема заключается в том, что вы должны убедиться, что ничто не будет пытаться выполнить код в общей библиотеке после того, как он был отключен, иначе вы получите ошибку сегментации.

Самый распространенный способ потерпеть неудачу - это создать объект, для которого деструктор определен в или вызывает код, определенный в общей библиотеке. Если объект все еще существует после dlclose(), ваше приложение будет аварийно завершать работу при удалении объекта.

Если вы посмотрите на logcat, вы должны увидеть трассировку стека отладки. Если вы можете расшифровать его с помощью инструмента arm-eabi-addr2line, вы сможете определить, находится ли он в деструкторе и, если да, для какого класса. В качестве альтернативы возьмите аварийный адрес, удалите старшие 12 бит и используйте его в качестве смещения в библиотеке, равной dlclose() d, и попытайтесь выяснить, какой код живет по этому адресу.

0 голосов
/ 23 июня 2011

Вы должны использовать extern "C", чтобы объявить вашу функцию aaa ()

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...