C++ Prevent linker from discarding functions - android

I am building a C++ library for android. I have X.so shared library which ends up in the android app and it's accessed through JNI. I also have a Y.a static library which has few generic functions that is used by X.so. Y.a also has some JNI interface functions as well that should be accessible by the android app.
Currently the problem I'm having is that, after building Y.a, I can see all the symbols I need to be exported. But after linking that to X.so, linker discards all the JNI interface functions because they are not used internally. I tried the following 2 options without any luck,
1.
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL myImportantFunction(JNIEnv*, jclass);
.
.
.
void* volatile tmp = (void*)&myImportantFunction;
#ifdef __cplusplus
}
#endif
2.
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void __attribute__((used)) JNICALL myImportantFunction(JNIEnv*, jclass);
.
.
.
#ifdef __cplusplus
}
#endif
If there are any clang attributes or hacks which can force the linker not to discard specific functions I need (when I'm building Y.a) it would be ideal. Thank you all for any help in this regards.

If you want to keep the entire Y.a, then -Wl,--whole-archive -lY -Wl,--no-whole-archive is indeed the way to go.
If you want to keep only specific symbols from Y.a, but let the linker discard other (unused) object files from it, then you need to tell the linker that these functions are used, like so:
g++ -u myImportantFunction x1.o x2.o ... -lY -shared -o X.so

Related

Calling JNI C function in another source file

I'm trying to call a C function foo() from a JNI native-lib.cpp file. foo() is declared in foo.h and defined in foo.c. native-lib.cpp already included "foo.h", so Android Studio seems to recognize that the function exists, but upon build, I get the following error:
error: undefined reference to 'foo(unsigned char const*, unsigned int*, int)'
foo() is declared as such in foo.h:
void foo(const unsigned char key[], unsigned int w[], int keysize);
foo() is called in native-lib.cpp as follows:
static unsigned char byteArray[32];
unsigned int intArray[64];
foo(byteArray, intArray, 256);
Am I missing anything? Why am I getting the error?
I also notice that when I open my foo.c, Android Studio informs me that "This file is not part of the project. Please include it in the appropriate build file (build.gradle, CMakeLists.txt or Android.mk etc.) and sync the project." How do I do that?
The probable issue is that your foo() function is being recognized by the linker as a C++ function, and not a C function.
Since C++ functions can be overloaded, the C++ compiler "mangles" the name so that the linker has a unique key to the function. Any C++ module that encounters foo.h will assume the function is a C++ function, thus name mangling will be done on the function name.
For example, possibly the mangled name looks something like this in the compiled object code:
foo#ucharp#uintp#int
(each brand of C++ compiler will have its own naming scheme).
On the other hand, when the C compiler encounters foo.h, the compiled object code produced a symbol representing an "unmangled" foo name, more or less, this:
foo or _foo
So now at link time, since you called the foo function from C++ code, the linker will attempt to find the mangled version, and since there is no C++ function named foo, you get the linker error.
So basically your foo.h header or your usage of it needs to be smart and work for C modules and C++ modules, where the C++ modules are aware that foo is referring to a C function and thus will not mangle the name.
One way to do this is to change foo.h to this:
#ifdef __cplusplus
extern "C" {
#endif
void foo(const unsigned char key[], unsigned int w[], int keysize);
#ifdef __cplusplus
}
#endif
So basically, when the header is seen by the C compiler the __cplusplus doesn't exist. For C++ modules, the __cplusplus is defined, and thus the extern "C" takes effect, thus the C++ modules recognize foo() as a C function.
If you can't change foo.h, then you can surround it with the extern "C" in your C++ modules:
extern "C" {
#include <foo.h>
}
See this on more information of __cplusplus

Compile two projects on NDK w/ gradle, one of which depends on the other's binary

I'm compiling a library in gradle like this:
externalNativeBuild {
// Encapsulates your CMake build configurations.
cmake {
// Provides a relative path to your CMake build script.
path "../../JRTPLIB/CMakeLists.txt"
}
}
I need to compile another source, probably using cmake or any other tool, also inside gradle, that will link to the compiled object from the lib above (JRTPLIB).
The obvious way would be to include the source of the library above in the lib I want to use and just link, but I need to do it separatedly.
The other way would be to rely on the generated library object at android/app/build/intermediates/cmake/... but first, it's supposed to have a debug binary and a release one in this folder, making it more coplicated to link, and secondly, it's not an elegant solution.
So how to do it?
Here's a simple example that accomplishes something like what I think you're trying to do.
Two libraries are being built and packaged into the app, where the second library depends on the first one. I chose to have lib1 and lib2 as part of the current project, but they could really be located anywhere.
app/CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
add_subdirectory(src/main/cpp/lib1)
add_subdirectory(src/main/cpp/lib2)
app/src/main/cpp/lib1/CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
native-lib1
SHARED
native-lib1.cpp )
target_include_directories(native-lib1 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
app/src/main/cpp/lib2/CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
native-lib2
SHARED
native-lib2.cpp )
find_library(log-lib log)
# native-lib2 depends on log and native-lib1
target_link_libraries(native-lib2 ${log-lib} native-lib1)
app/src/main/cpp/lib1/native-lib1.h
#pragma once
int foo();
app/src/main/cpp/lib1/native-lib1.cpp
#include "native-lib1.h"
int foo()
{
return 42;
}
app/src/main/cpp/lib2/native-lib2.cpp
#include <jni.h>
#include <android/log.h>
#include <string>
#include "native-lib1.h"
extern "C" JNIEXPORT jstring JNICALL Java_com_example_cmaketwolibs_MainActivity_stringFromJNI(
JNIEnv *env, jobject thiz) {
// Call function from native-lib1
__android_log_print(ANDROID_LOG_WARN,
"native-lib2", "Calling native-lib1\'s foo(): %d",
foo());
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}

Obscuring static strings in Android with the NDK: How to extract strings only from within a .so-file

In my Android App I want to protecte some static strings from being read by decomnpiling the application. I read that a way to do so is to put them into c++ code and load them natively into the Android project.
so I created a c++ file and a CMake script. The c++ file:
#include <jni.h>
#include <string>
extern "C"
JNIEXPORT jstring JNICALL
Java_package_name_NdkUtils_methodForStringToBeProtected(
JNIEnv *env, jobject /* this */) {
std::string result = "secretStaticStringHere";
return env->NewStringUTF(result.c_str());
}
and the CMakeLists.txt:
cmake_minimum_required(VERSION 3.4.1)
add_library(native-lib SHARED src/main/cpp/native-lib.cpp )
find_library( og-lib log )
target_link_libraries( native-lib ${log-lib} )
In Android now I suceeded getting that string within the native call.
What I want to get is that the c++ is hidden from the built. I only want to include the so files.
So I went into the genrated apk and I was able to extract the libnative-lib.so file. I integrated the so-files in the aap/src/main/jniLibs folder. But when I remove the c++ sources I cannot access the methodForStringToBeProtected-method anymore. What do I have to do now to call the function only from within the so-file?

Type could not be resolved in ndk

In android ndk when i use the header files that is generated by helper tool javah it works fine. but when i create a normal file with .h extension and include jni.h and any other required header files i am not able use the type or keyword or any functions from the included header files and it always shows me the "Type 'whatever' couldn't be resolved" while this same thing i can do in the machine generated header files with javah tools without any cause.
Though i've included arm platforms library in C/C++ General -> Paths and Symbols ->Include. it keeps showing me this error.
Consider the following piece of code.
#include "store.h"
#include <jni.h>
#include <stdint.h>
#include <pthread.h>
#ifndef _STOREWATCHER_H_
#define _STOREWATCHER_H_
#define SLEEP_DURATION 5
#define STATE_OK 0
#define STATE_KO 1
#ifdef __cplusplus
extern "C" {
#endif
typedef struct{
Store* mStore;
JavaVM* mJavaVM;
jobject mStoreFront;
pthread_t mThread;
int32_t mState;
}StoreWatcher;
#ifdef __cplusplus
}
#endif
#endif
it shows me in this code that Store, JavaVM, jobject, pthread_t couldn't be resolved. Please help me.
any kind of help will be appreciated.
In C, unlike Java, you have files with names like storewatcher.c and files with names like storewatcher.h. The .h files are not compiled on themselves, you must use #include directive in one or more .c files for the .h file to be recognized by the compiler, e.g.
#include "storewatcher.h"

Android std and stl support

I am playing with android ndk. I am using Window Vista with cygwin (latest version). I compiled and launched the hello world jni sample on my phone. It is working. The code is (is a .cpp file):
#include <string.h>
#include <jni.h>
extern "C" {
JNIEXPORT jstring JNICALL Java_org_android_helloworld_HelloworldActivity_invokeNativeFunction(JNIEnv* env, jobject javaThis);
};
jstring Java_org_android_helloworld_HelloworldActivity_invokeNativeFunction(JNIEnv* env, jobject javaThis)
{
return env->NewStringUTF("Hello from native code!");
}
I wanted to add some modifications, just to play with it a bit:
#include <algorithm>
and then, in the function above, i added:
int a;
a=std::min<int>(10, 5);
but the compiler says that it cannot find the file 'algorithm' and that min() is not part of std.
After a bit of searching, i have found that the android ndk has a gnu-libstdc++ directory with all the std files needed. Reading the NDK docs, i have learned that usint std::* should work without any modification to the code (if one include the proper header files). But it seems that gcc on cygwin is not able to find the needed files.
What are the steps to do in order to be able to use std and stl within a .cpp file in an android ndk app?
From NDK r5's docs/CPLUSPLUS-SUPPORT.html:
By default, the headers and libraries for the minimal C++ runtime system
library (/system/lib/libstdc++.so) are used when building C++ sources.
You can however select a different implementation by setting the variable
APP_STL to something else in your Application.mk, for example:
APP_STL := stlport_static
To select the static STLport implementation provided with this NDK.
Value APP_STL values are the following:
system -> Use the default minimal C++ runtime library.
stlport_static -> Use STLport built as a static library.
stlport_shared -> Use STLport built as a shared library.
gnustl_static -> Use GNU libstdc++ as a static library.
Which NDK are you using? Have you tried compiling one of the sample applications that utilize the STL such as test-libstdc++?

Categories

Resources