I have some problems when using the dynamic loading API (<dlfcn.h>: dlopen(), dlclose(), etc) on Android.
I'm using NDK standalone toolchain (version 8) to compile the applications and libraries.
The Android version is 2.2.1 Froyo.
Here is the source code of the simple shared library.
#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);
}
Here is the program source code which uses the mentioned library.
#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;
}
With these sources everything is working fine, but when I try to use some STL functions or classes, the program crashes with a segmentation fault, when the main() function exits, for example when using this source code for the shared library.
#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;
}
With this code, the program crashes with segmentation fault after or the during main() function exit.
I have tried couple of tests and found the following results.
Without using of STL everything is working fine.
When use STL and do not call dlclose() at the end, everything is working fine.
I tried to compile with various compilation flags like -fno-use-cxa-atexit or -fuse-cxa-atexit, the result is the same.
What is wrong in my code that uses the STL?
Looks like I found the reason of the bug. I have tried another example with the following source files:
Here is the source code of the simple class:
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]);
}
}
}
As you can see from the code I did not used any STL related stuff.
Here is the source files of the functions library exports.
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;
}
And finally this is the source code of the programm that uses the library.
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;
}
Again as you can see the program using the library also does not using any STL related stuff, but after run of the program I got the same segmentation fault during main(...) function exit. So the issue is not connected to STL itself, and it is hidden in some other place. Then after some long research I found the bug.
Normally the destructors of static C++ variables are called immediately before main(...) function exit, if they are defined in main program, or if they are defined in some library and you are using it, then the destructors should be called immediately before dlclose(...).
On Android OS all destructors(defined in main program or in some library you are using) of static C++ variables are called during main(...) function exit. So what happens in our case? We have cls static C++ variable defined in library we are using. Then immediately before main(...) function exit we call dlclose(...) function, as a result library closed and cls becomes non valid. But the pointer of cls is stored somewhere and it's destructor should be called during main(...) function exit, and because at the time of call it is already invalid, we get segmentation fault. So the solution is to not call dlclose(...) and everything should be fine. Unfortunately with this solution we cannot use attribute((destructor)) for deinitializing of something we want to deinitialize, because it is called as a result of dlclose(...) call.
I have a general aversion to calling dlclose(). The problem is that you must ensure that nothing will try to execute code in the shared library after it has been unmapped, or you will get a segmentation fault.
The most common way to fail is to create an object whose destructor is defined in or calls code defined in the shared library. If the object still exists after dlclose(), your app will crash when the object is deleted.
If you look at logcat you should see a debuggerd stack trace. If you can decode that with the arm-eabi-addr2line tool you should be able to determine if it's in a destructor, and if so, for what class. Alternatively, take the crash address, strip off the high 12 bits, and use that as an offset into the library that was dlclose()d and try to figure out what code lives at that address.
I encountered the same headache on Linux. A work-around that fixes my segfault is to put these lines in the same file as main(), so that dlclose() is called after main returns:
static void* handle = 0;
void myDLClose(void) __attribute__ ((destructor));
void myDLClose(void)
{
dlclose(handle);
}
int main()
{
handle = dlopen(...);
/* ... real work ... */
return 0;
}
The root cause of dlclose-induced segfault may be that a particular implementation of dlclose() does not clean up the global variables inside the shared object.
You need to compile with -fpic as a compiler flag for the application that is using dlopen() and dlclose(). You should also try error handling via dlerror() and perhaps checking if the assignment of your function pointer is valid, even if it's not NULL the function pointer could be pointing to something invalid from the initialization, dlsym() is not guaranteed to return NULL on android if it cannot find a symbol. Refer to the android documentation opposed to the posix compliant stuff, not everything is posix compliant on android.
You should use extern "C" to declare you function aaa()
Related
I am hooking an existing library that is compiled using gnustl std::string instead of libc++ std::_ndk1::string. If I try to set or access these strings I just get garbage. How do I convert my std::string to std::_ndk1::string and vice versa in my hook application?
I cannot compile my hook with "-DANDROID_STL=gnustl_shared" because it no longer exists and other libraries in use require libc++.
The documentation mentions this https://developer.android.com/ndk/guides/cpp-support "The various STLs are not compatible with one another. As an example, the layout of std::string in libc++ is not the same as it is in gnustl" which is exactly the problem.
To use gnustl, you could compile all your native code with NDK r.17 or older. This is a dangerous path, because many important bugs, including security-related, have been fixed since then.
Another unsupported (and not recommended) way to deal with your problem is to get the gnustl sources from NDK r.17 and compile them with the latest NDK version.
Your best option is to have all your dependencies rebuilt with a recent version of NDK and its c++_shared runtime library.
Here's what I came up with (for now, really not ideal):
StringsProxy.cc
#include "StringsProxy.h"
#include <iostream>
#include <string>
using namespace std;
__attribute__((visibility("default")))
extern "C" StringsProxy::StringsProxy(const char* contents)
{
set_string = std::string(contents);
}
__attribute__((visibility("default")))
extern "C" StringsProxy::StringsProxy(uintptr_t str) {
set_string = *reinterpret_cast<proxy_string*>(str);
}
__attribute__((visibility("default")))
extern "C" const char* StringsProxy::c_str() {
return set_string.c_str();
}
__attribute__((visibility("default")))
extern "C" const uintptr_t* StringsProxy::ptr() {
return reinterpret_cast<uintptr_t *>(&set_string);
}
__attribute__((visibility("default")))
extern "C" StringsProxy::~StringsProxy() {
}
StringsProxy.h
#ifndef __STRINGSPROXY_H__
#define __STRINGSPROXY_H__
#include <string>
typedef std::basic_string<char> proxy_string;
class StringsProxy
{
public:
/* Initialize StringsProxy with a pointer to an existing string */
StringsProxy(uintptr_t str);
/* Initialize StringsProxy with a new string */
StringsProxy(const char* str);
/* Get C string */
virtual const char* c_str();
/* Get pointer to string for injection */
const virtual uintptr_t* ptr();
private:
proxy_string set_string;
};
#endif
Compile this into a shared object using the old NDK with -DCMAKE_ANDROID_STL_TYPE=gnustl_static
Then link this shared object to the hooking program (in CMakeLists):
target_link_libraries(${TARGET_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/abiproxy/build/armeabi-v7a/libabiproxy.so)
Then in the hooking program, can be used like this:
#include "abiproxy/StringsProxy.h"
void *setUriDebug(uintptr_t a1, uintptr_t stri) {
auto y = StringsProxy(stri);
LOGI("URI CALLED %s", y.c_str());
return setUriDebugOld(a1, stri);
}
Or in reverse:
StringsProxy assetNameBaseProxy = StringsProxy("https://example.com/");
void setResourceUrl(uintptr_t* a1, int a2) {
*(a1 + 1) = *assetNameBaseProxy.ptr();
}
This isn't by any means a good solution, but it works for my use-case.
I'm new to stackoverflow and I wish to ask for some help on Android C++.
I'm trying to implement a very simple Android test program in C++ which calls a function in a loaded shared library implemented in C++ as well.
Here is my main JNI implemenation (native-lib.cpp):
#include <jni.h>
#include <string>
#include <dlfcn.h>
#include "external.hpp"
extern "C" JNIEXPORT jstring JNICALL
Java_com_useless_myapplication_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
void* handle;
void (*funcext)();
handle = dlopen("libexternal.so",RTLD_LAZY);
funcext = (void (*)(void))dlsym(handle, "_Z5func2v");
try {
funcext();
}
catch (MyException &err)
{
std::string hello = "MyException from C++";
return env->NewStringUTF(hello.c_str());
}
catch (GenericException &err)
{
std::string hello = "GenericException from C++";
return env->NewStringUTF(hello.c_str());
}
catch (GenericException* err)
{
std::string hello = "GenericException* from C++";
return env->NewStringUTF(hello.c_str());
}
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
And here is my libexternal.so implementation (external.cpp):
#include <jni.h>
#include <string.h>
#include "external.hpp"
GenericException::GenericException(){};
GenericException::GenericException(int errcode,char* msg)
{
errorcode = errcode;
memset(message,0,256);
strcpy(message,msg);
}
MyException::MyException(int errcode,char* msg)
{
errorcode = errcode;
memset(message,0,256);
strcpy(message,msg);
}
void func()
{
throw MyException(10,"Error1!");
}
bool func3()
{
try {
func();
}
catch (GenericException &err)
{
return false;
}
return true;
}
void func2()
{
if (!func3())
throw MyException(11,"Error2!");
}
The external.hpp file is defined as follows:
void func();
void func2();
bool func3();
class GenericException
{
public:
GenericException();
GenericException(int errcode,char* msg);
protected:
int errorcode;
char message[256];
};
class MyException : public GenericException
{
public:
MyException(int errcode,char* msg);
};
The program compiles and links clean however when I run it my android application crashes whith the following message in the logcat:
2018-11-14 09:57:42.058 6519-6519/com.useless.myapplication A/libc: /usr/local/google/buildbot/src/android/ndk-release-r18/external/libcxx/../../external/libcxxabi/src/abort_message.cpp:73: abort_message: assertion "terminating with uncaught exception of type MyException" failed
The error rises when I try to execute external.cpp line 41:
throw MyException(11,"Error2!");
As suggested by other posts I've found I've tried to enable the -frtti flag in my app build.gradle cppflgs but this doesn't solved the error.
I've tried to run the same code (without the Java top layer honestly) on Linux and MacOS but on these platforms the exception is caught by the native-lib.cpp code.
Is there something I'm not aware of about C++ exceptions in Android?
How can I be able to catche the exception thrown by a library I loaded with dlopen on Android?
Thanks
You exception type does not have a key function, so it's typeinfo is emitted with vague linkage. The means it is a weak symbol in every library in which it is used.
Your JNI library loads and resolves its own typeinfo. Your dlopened library is then loaded and also resolves its own typeinfo because it cannot access the parent scope (System.loadLibrary uses RTLD_LOCAL). Because of this, there are two separate typeinfo objects for your exception type. RTTI equality is checked by comparing the addresses of the typeinfo object (see the C++ ABI spec).
I'm not certain if this can be resolved without directly linking your JNI code to libexternal.so. If you add the key function necessary to make this work (which would be defined in libexternal.so), then I believe you'd need to link to it for your JNI code to link.
I have a.so which defines void a() and b.so which defines void b(). They are both put in the .apk so they are available to the Android application.
Now suppose that I'm calling a() through JNI. Is it possible to call b() from a() while completely bypassing JNI?
Can I do it this way in Android (the code is only for illustration, so it might have some errors)?
void a() {
void *handle = dlopen("b.so", RTLD_LAZY);
void (*b)() = dlsym(handle, "b");
b();
}
Would I need to add the fully qualified path, or is b.so already in LD_LIBRARY_PATH for the app?
You can do it this way on Android, though take care of where the shared library has been put in Android folders. It can change from a version to another.
On api 17 for example, it remains in /data/app-lib/. You can hardwrite it, but the best is to make calls to Java (through JNI) to know where the libraries should be.
We're doing something like this in our project :
JNIEnv* env;
const char* temp;
jobject oActivity = state->activity->clazz;
jclass cActivity = env->GetObjectClass(oActivity);
// get the path to where android extracts native libraries to
jmethodID midActivityGetApplicationInfo = env->GetMethodID(cActivity, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
jobject oApplicationInfo = env->CallObjectMethod(oActivity, midActivityGetApplicationInfo);
jclass cApplicationInfo = env->GetObjectClass(oApplicationInfo);
jfieldID fidApplicationInfoNativeLibraryDir = env->GetFieldID(cApplicationInfo, "nativeLibraryDir", "Ljava/lang/String;");
jstring sNativeLibraryDir = (jstring)env->GetObjectField(oApplicationInfo, fidApplicationInfoNativeLibraryDir);
temp = env->GetStringUTFChars(sNativeLibraryDir, NULL);
strcpy(libpath, temp);
strcat(libpath, "/");
Then you push your dlopen + dlsym combo in the fight and it should work.
As mentioned here : How do I load a shared object in C++?
There are two ways of loading shared objects in C++
For either of these methods you would always need the header file for the object you want to use. The header will contain the definitions of the classes or objects you want to use in your code.
#include "blah.h"
int main()
{
ClassFromBlah a;
a.DoSomething();
}
gcc yourfile.cpp -lblah
Dynamically (In Linux):
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
int main(int argc, char **argv) {
void *handle;
double (*cosine)(double);
char *error;
handle = dlopen ("libm.so", RTLD_LAZY);
if (!handle) {
fprintf (stderr, "%s\n", dlerror());
exit(1);
}
dlerror(); /* Clear any existing error */
cosine = dlsym(handle, "cos");
if ((error = dlerror()) != NULL) {
fprintf (stderr, "%s\n", error);
exit(1);
}
printf ("%f\n", (*cosine)(2.0));
dlclose(handle);
return 0;
}
PS : for the dynamic approach, it depends on platform : on Linux, you use dlopen, on windows, you use LoadLibrary.
I've wrote a simple application to use QSettings. Can someone tell me what wrong I'm doing here..
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QWidget *pMainWidget;
QHBoxLayout *pMainLayout;
QSettings *pSetting;
QLabel *pLabel;
QPushButton *pButtonShow;
QPushButton *pButtonSet;
QLineEdit *pLineEdit;
QString pSettingFile;
public slots:
void showSettingData();
void setData();
};
mainwindow.cpp
#include "mainwindow.h"
#include <QtCore/QCoreApplication>
#include <QApplication>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
pMainWidget = new QWidget (parent);
pMainLayout = new QHBoxLayout(pMainWidget);
pLabel = new QLabel("Output comes here",pMainWidget);
pLineEdit = new QLineEdit();
pButtonShow = new QPushButton("Show", pMainWidget);
pButtonSet = new QPushButton("Set", pMainWidget);
setCentralWidget(pMainWidget);
pMainWidget->setLayout(pMainLayout);
pMainLayout->addWidget(pLabel);
pMainLayout->addWidget(pButtonShow);
pMainLayout->addWidget(pLineEdit);
pMainLayout->addWidget(pButtonSet);
pSettingFile = QApplication::applicationDirPath()+"settings.ini";
QObject::connect(pButtonShow, SIGNAL(clicked()), this, SLOT(showSettingData()));
QObject::connect(pButtonSet, SIGNAL(clicked()), this, SLOT(setData()));
}
MainWindow::~MainWindow()
{
}
void MainWindow::setData()
{
QSettings Setting(pSettingFile, QSettings::NativeFormat);
QString data = pLineEdit->text();
Setting.setValue("baseurl", data);
}
void MainWindow::showSettingData()
{
QSettings Setting(pSettingFile, QSettings::NativeFormat);
if (Setting.contains("baseurl"))
{
QString data = Setting.value("baseurl").toString();
pLabel->setText(data);
}
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
When I debug, it doesn't returns false for "Setting.contains("baseurl")"... Do we have to create the setting file?
The issue is that you are not calling sync after setting the data. Try to insert this line after the setting:
Setting.setValue("baseurl", data);
Setting.sync();
This should not be needed on Windows, but it seems to be necessary on Windows.
Also, as suggested in comments, I would suggest to use QSettings::IniFormat instead of QSettings::NativeFormat if you really want to use files rather than potentially registry on Windows.
You should also consider making the settings object as a class member rather than constructing it all the time.
Also, note that you may be using the wrong path unintentionally because you would have to add the "slash" character explicitly before the "settings.ini" file name. This is just a side note, however.
Device: Google Nexus 7 (2013).
logcat contains
I/DEBUG ( 176): Abort message: '### ABORTING: invalid address or address of corrupt block 0x5c6b0 passed to dlfree'.
I think there is a problem with static libgnustl. Am I right? How can I force to use ibgnustl_shared using GNU Build System? I uses android-ndk-r9b installed into $HOME/android-ndk-r9b. Firstly I made standalone toolchain
$HOME/android-ndk-r9b/build/tools/make-standalone-toolchain.sh --platform=android-9 --install-dir=$HOME/android-toolchain
I wrote simple test that contains shared library and console program that uses it. It crashes on device.
// libtest.h
#ifndef TEST_H
#define TEST_H
#include <stddef.h>
#include <string>
namespace test
{
enum { MSG_LEN_BYTES = 3, MSG_LEN_MAX = 0xFFF };
int encodeMsgLength(std::string &encoded_length, size_t length);
}
#endif
// libtest.cpp
#include "libtest.h"
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
int test::encodeMsgLength(std::string &encoded_length, size_t length)
{
encoded_length.clear();
if (length > MSG_LEN_MAX) {
return E2BIG;
}
char buf[MSG_LEN_BYTES + 1] = {0};
int cnt = snprintf(buf, sizeof(buf), "%03zX", length);
if (cnt == MSG_LEN_BYTES && static_cast<size_t>(cnt) < sizeof(buf)) {
encoded_length.assign(buf, cnt);
return 0;
}
assert(!"encodeMsgLength");
return EBADF;
}
// test.cpp
#include <iostream>
#include <stdlib.h>
#include "libtest.h"
int main(int argc, char* argv[])
{
if (argc != 2) {
std::cout << "Usage: test <number>\n";
return 1;
}
size_t len = atoi(argv[1]);
std::cout << "encodeMsgLength(" << len << ") => ";
std::string str;
int err = test::encodeMsgLength(str, len);
std::cout << str << ", error " << err << std::endl;
return 0;
}
Makefile.am
lib_LTLIBRARIES = libtest.la
libtest_la_LDFLAGS = -shared -avoid-version
libtest_la_SOURCES = libtest.cpp
bin_PROGRAMS = test
test_LDADD = libtest.la
test_SOURCES = test.cpp
Build your app using the default : ndk-build --TARGET_PLATFORM android-9
To make sure that only shared library is taken edit your Android.mk and put appropriate library like LOCAL_SHARED_LIBS := libgnustl
There are various other tools that you can use to debug your issue:
1. gdb [search for remote debugging android native app]
2. strace [ if not directly available use busybox strace ]
3. valgrind
Also in many cases the memory stack during crash are stores in /data/tombstones. Locate your errror file, pull it and show the details here of the same.
Instead of linking to libgnustl-static.a, use libgnustl-shared.so. Your observation makes sense: when both libtest.so and test are linked with the static version, then the std::string class will have two different vtables, and the program may crash on shutdown. libstlport uses internally system stl (always shared) for new/delete, and thus behaves better in your test case. This, naturally, does not protect from other clashes, which may also be lethal. That's why you should use shared version of stl if you use it in two or more dynamically linked components.
In CPLUSPLUS-SUPPORT.html
"II.3. Static runtimes:
Please keep in mind that the static library variant of a given C++ runtime SHALL ONLY BE LINKED INTO A SINGLE BINARY for optimal conditions."
I had an issue with this where malloc was crashing.