termux-x11/app/src/main/jni/jniLoader/dt_needed_main.c

233 lines
5.4 KiB
C

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <elf.h>
#include <errno.h>
#include <unistd.h>
#include <libgen.h>
#include "log.h"
#ifdef __ANDROID__
#include <jni.h>
int System_load(JNIEnv *env, char *filename);
#endif
#ifndef __ANDROID__
#define __USE_GNU
#include <dlfcn.h>
#include <execinfo.h>
int trace_funcs = 0;
void __attribute__((no_instrument_function))
__cyg_profile_func_enter (void *func, void *caller) {
if (!trace_funcs) return;
Dl_info info;
if (dladdr(func, &info))
LOGD ("enter [%s] %s", (info.dli_fname) ? info.dli_fname : "?", info.dli_sname ? info.dli_sname : "?");
}
void __attribute__((no_instrument_function))
__cyg_profile_func_exit (void *func, void *caller) {
if (!trace_funcs) return;
Dl_info info;
if (dladdr(func, &info))
LOGD ("leave [%s] %s", (info.dli_fname) ? info.dli_fname : "?", info.dli_sname ? info.dli_sname : "?");
}
#endif
#define static // backtrace do not report static function names
#define bit64
#include "dt_needed.c"
#undef bit64
#include "dt_needed.c"
char ** str_split(char *s, char *delim) {
char *strings[128];
char *pch, **result, *str = strdup(s);
int i=0, j=0;
pch = strtok (str,delim);
while (pch != NULL) {
strings[i] = strdup(pch);
pch = strtok(NULL, ":");
i++;
}
result = calloc(i+1, sizeof(char*));
if (!result) {
LOGE("str_split: Failed to allocate result array: %s", strerror(errno));
return NULL;
}
for (j=0; j<i; j++)
result[j] = strings[j];
result[i] = NULL;
free(str);
return result;
}
char ** dlneeds(char *filename) {
Elf32_Ehdr hdr;
int fd = open( filename, O_RDONLY );
if( fd < 0 ) {
LOGE("Cannot open %s: %s", filename, strerror( errno ) );
return NULL;
}
if( read( fd, &hdr, sizeof( Elf32_Ehdr ) ) < 0 ) {
LOGE("%s: Cannot read elf header: %s", filename, strerror( errno ) );
return NULL;
}
lseek(fd, 0, SEEK_SET);
if (hdr.e_ident[EI_CLASS] == ELFCLASS32) return dlneeds32(fd);
if (hdr.e_ident[EI_CLASS] == ELFCLASS64) return dlneeds64(fd);
//Shoult never reach it.
return NULL;
}
char *find_realpath(char *filename) {
if (!filename) return NULL;
char *result = NULL;
int i;
char path[1024];
char *LD_LIBRARY_PATH="/data/data/com.termux/files/usr/lib:/system/lib"; // "/usr/lib:/usr/lib/x86_64-linux-gnu";
char **paths = str_split(strdup(LD_LIBRARY_PATH), ":");
if (paths == NULL) goto done;
for (i=0;paths[i]!=NULL;i++) {
sprintf(path, "%s/%s", paths[i], filename);
if(access(path, F_OK) != -1) {
result = strdup(path);
break;
}
}
done:
if (paths) {
for(i=0;paths[i]!=NULL;i++)
free(paths[i]);
free(paths);
}
return result;
}
static char *dl_skips[] = {
"libc.so",
"libdl.so",
NULL,
};
int dl_skipping(char* bname) {
int i;
for (i=0; dl_skips[i]!=NULL; i++) {
if (strcmp(dl_skips[i], bname)==0)
return 1;
}
return 0;
}
int android_dlopen(JNIEnv *env, char* filename) {
int i, j, r;
char *bname = basename(filename);
char *realpath = find_realpath(bname);
if (!realpath) {
LOGE("%s is not found", filename);
return 1;
}
char **dl_needs = dlneeds(realpath);
if (dl_needs)
for (i=0;;i++) {
int skipping = 0;
if (dl_needs[i] == NULL) break;
if (dl_skipping(filename)) break;
if (!skipping) {
r = android_dlopen(env, dl_needs[i]);
if (r) {
LOGE("Failed to dlopen %s", dl_needs[i]);
break;
}
}
}
free(realpath);
return System_load(env, realpath);
}
#ifndef __ANDROID__
int main(int argc, char **argv) {
if (getenv("TRACE_FUNCS") != NULL) trace_funcs = 1;
if( argc < 2 ) {
LOGE("Usage %s <binary>", argv[0]);
return 1;
}
android_dlopen(argv[1]);
return 0;
}
#endif
#ifdef __ANDROID__
JavaVM *jvm = NULL;
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
jvm = vm;
return JNI_VERSION_1_4;
}
int System_load(JNIEnv *env, char *filename) {
jclass System = (*env)->FindClass (env, "java/lang/System");
if(System == NULL) {
LOGE("ERROR: class java.land.System is not found!");
}
jmethodID load = (*env)->GetStaticMethodID(env, System, "load","(Ljava/lang/String;)V");
if (load == NULL) {
LOGE("ERROR: method System.load is not found!");
}
jstring file = (*env)->NewStringUTF(env, filename);
if (file == NULL) {
LOGE("failed to create jstring");
}
(*env)->CallStaticVoidMethod(env, System, load, file);
(*env)->DeleteLocalRef(env, file);
(*env)->DeleteLocalRef(env, System);
jboolean flag = (*env)->ExceptionCheck(env);
if (flag) {
jthrowable e = (*env)->ExceptionOccurred(env);
(*env)->ExceptionClear(env);
jclass clazz = (*env)->GetObjectClass(env, e);
jmethodID getMessage = (*env)->GetMethodID(env, clazz, "getMessage", "()Ljava/lang/String;");
jstring message = (jstring)(*env)->CallObjectMethod(env, e, getMessage);
const char *mstr = (*env)->GetStringUTFChars(env, message, NULL);
LOGE("%s", mstr);
(*env)->ReleaseStringUTFChars(env, message, mstr);
(*env)->DeleteLocalRef(env, message);
(*env)->DeleteLocalRef(env, clazz);
(*env)->DeleteLocalRef(env, e);
return 1;
}
return 0;
}
JNIEXPORT void JNICALL
Java_com_termux_wtermux_jniLoader__1loadLibrary(JNIEnv *env, jclass type, jstring name_) {
char *name = (char*) (*env)->GetStringUTFChars(env, name_, 0);
android_dlopen(env, name);
(*env)->ReleaseStringUTFChars(env, name_, name);
}
#endif