change xhook to bytehook
This commit is contained in:
parent
ed4cf7c99a
commit
fd9fe6b4f8
|
@ -100,6 +100,7 @@ android {
|
|||
jniLibs {
|
||||
useLegacyPackaging true
|
||||
}
|
||||
pickFirst '**/libbytehook.so'
|
||||
}
|
||||
splits {
|
||||
def arch = System.getProperty("arch", "all")
|
||||
|
|
|
@ -33,10 +33,13 @@ android {
|
|||
}
|
||||
}
|
||||
ndkVersion '25.1.8937393'
|
||||
buildFeatures {
|
||||
prefab true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation 'com.bytedance:bytehook:1.0.9'
|
||||
implementation 'com.jaredrummler:android-device-names:2.1.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.11.0'
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package com.oracle.dalvik;
|
||||
|
||||
public final class VMLauncher {
|
||||
private VMLauncher() {
|
||||
}
|
||||
public static native int launchJVM(String[] args);
|
||||
}
|
|
@ -9,6 +9,7 @@ import android.system.Os;
|
|||
import android.util.ArrayMap;
|
||||
|
||||
import com.jaredrummler.android.device.DeviceName;
|
||||
import com.oracle.dalvik.VMLauncher;
|
||||
import com.tungsten.fclauncher.bridge.FCLBridge;
|
||||
import com.tungsten.fclauncher.plugins.FFmpegPlugin;
|
||||
import com.tungsten.fclauncher.utils.Architecture;
|
||||
|
@ -234,6 +235,7 @@ public class FCLauncher {
|
|||
String nativeDir = config.getContext().getApplicationInfo().nativeLibraryDir;
|
||||
|
||||
bridge.dlopen(nativeDir + "/libopenal.so");
|
||||
bridge.dlopen(nativeDir + "/" + config.getRenderer().getGlLibName());
|
||||
}
|
||||
|
||||
private static void launch(FCLConfig config, FCLBridge bridge, String task) throws IOException {
|
||||
|
@ -260,10 +262,10 @@ public class FCLauncher {
|
|||
isToken = true;
|
||||
log(bridge, prefix + arg);
|
||||
}
|
||||
bridge.setupJLI();
|
||||
bridge.setLdLibraryPath(getLibraryPath(config.getContext(), config.getJavaPath()));
|
||||
log(bridge, "Hook exit " + (bridge.setupExitTrap(bridge) == 0 ? "success" : "failed"));
|
||||
int exitCode = bridge.jliLaunch(args);
|
||||
bridge.setupExitTrap(bridge);
|
||||
log(bridge, "Hook success");
|
||||
int exitCode = VMLauncher.launchJVM(args);
|
||||
bridge.onExit(exitCode);
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,6 @@ public class FCLBridge implements Serializable {
|
|||
private SurfaceTexture surfaceTexture;
|
||||
|
||||
static {
|
||||
System.loadLibrary("xhook");
|
||||
System.loadLibrary("fcl");
|
||||
System.loadLibrary("fcl_awt");
|
||||
}
|
||||
|
@ -101,12 +100,10 @@ public class FCLBridge implements Serializable {
|
|||
public native void setenv(String key, String value);
|
||||
public native int dlopen(String path);
|
||||
public native void setLdLibraryPath(String path);
|
||||
public native int setupExitTrap(FCLBridge bridge);
|
||||
public native void setupExitTrap(FCLBridge bridge);
|
||||
public native void setEventPipe();
|
||||
public native void pushEvent(long time, int type, int keycode, int keyChar);
|
||||
public native void refreshHitResultType();
|
||||
public native void setupJLI();
|
||||
public native int jliLaunch(String[] args);
|
||||
|
||||
public native void setFCLBridge(FCLBridge fclBridge);
|
||||
|
||||
|
|
|
@ -12,29 +12,14 @@ LOCAL_SRC_FILES := tinywrapper/main.c tinywrapper/string_utils.c
|
|||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/tinywrapper
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := xhook
|
||||
LOCAL_SRC_FILES := xhook/xhook.c \
|
||||
xhook/xh_core.c \
|
||||
xhook/xh_elf.c \
|
||||
xhook/xh_jni.c \
|
||||
xhook/xh_log.c \
|
||||
xhook/xh_util.c \
|
||||
xhook/xh_version.c
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/xhook/include
|
||||
LOCAL_CFLAGS := -Wall -Wextra -Werror -fvisibility=hidden
|
||||
LOCAL_CONLYFLAGS := -std=c11
|
||||
LOCAL_LDLIBS := -llog
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := fcl
|
||||
LOCAL_SHARED_LIBRARIES := xhook
|
||||
LOCAL_SHARED_LIBRARIES := bytehook
|
||||
LOCAL_SRC_FILES := fcl/fcl_bridge.c \
|
||||
fcl/fcl_event.c \
|
||||
fcl/fcl_loader.c
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/xhook/include \
|
||||
$(LOCAL_PATH)/fcl/include
|
||||
fcl/fcl_loader.c \
|
||||
fcl/jre_launcher.c
|
||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/fcl/include
|
||||
LOCAL_LDLIBS := -llog -ldl -landroid
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
|
@ -346,4 +331,5 @@ LOCAL_SRC_FILES := lwjgl/opengl/org_lwjgl_opengl_AMDDebugOutput.c \
|
|||
lwjgl/opengl/org_lwjgl_opengl_NVXProgressFence.c \
|
||||
lwjgl/opengl/org_lwjgl_opengl_OVRMultiview.c
|
||||
LOCAL_CFLAGS := -O2 -Wall -c -fPIC -std=c99 -Wunused -DLWJGL_FCL -Wunused-value
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
$(call import-module,prefab/bytehook)
|
|
@ -65,7 +65,6 @@ JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setFCLBridg
|
|||
}
|
||||
|
||||
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
env_init();
|
||||
if (fcl->android_jvm == NULL) {
|
||||
fcl->android_jvm = vm;
|
||||
JNIEnv* env = 0;
|
||||
|
|
|
@ -8,25 +8,12 @@
|
|||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
#include <android/log.h>
|
||||
#include <xhook.h>
|
||||
#include <bytehook.h>
|
||||
#include <string.h>
|
||||
#include <fcl_internal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define FULL_VERSION "1.8.0-internal"
|
||||
#define DOT_VERSION "1.8"
|
||||
#define PROGNAME "java"
|
||||
#define LAUNCHER_NAME "openjdk"
|
||||
|
||||
static char* const_progname = PROGNAME;
|
||||
static const char* const_launcher = LAUNCHER_NAME;
|
||||
static const char** const_jargs = NULL;
|
||||
static const char** const_appclasspath = NULL;
|
||||
static const jboolean const_cpwildcard = JNI_TRUE;
|
||||
static const jboolean const_javaw = JNI_FALSE;
|
||||
static const jint const_ergo_class = 0; //DEFAULT_POLICY
|
||||
|
||||
typedef void (*android_update_LD_LIBRARY_PATH_t)(const char*);
|
||||
static volatile jobject exitTrap_bridge;
|
||||
static volatile jmethodID exitTrap_method;
|
||||
|
@ -35,6 +22,7 @@ static volatile jmethodID log_method;
|
|||
static JavaVM *log_pipe_jvm;
|
||||
static int fclFd[2];
|
||||
static pthread_t logger;
|
||||
static _Atomic bool exit_tripped = false;
|
||||
|
||||
void correctUtfBytes(char *bytes) {
|
||||
char three = 0;
|
||||
|
@ -204,72 +192,80 @@ JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setLdLibrar
|
|||
(*env)->ReleaseStringUTFChars(env, ldLibraryPath, ldLibPathUtf);
|
||||
}
|
||||
|
||||
void (*old_exit)(int code);
|
||||
void custom_exit(int code) {
|
||||
__android_log_print(code == 0 ? ANDROID_LOG_INFO : ANDROID_LOG_ERROR, "FCL", "JVM exit with code %d.", code);
|
||||
typedef void (*exit_func)(int);
|
||||
|
||||
_Noreturn static void nominal_exit(int code) {
|
||||
JNIEnv *env;
|
||||
(*exitTrap_jvm)->AttachCurrentThread(exitTrap_jvm, &env, NULL);
|
||||
(*env)->CallVoidMethod(env, exitTrap_bridge, exitTrap_method, code);
|
||||
jint errorCode = (*exitTrap_jvm)->GetEnv(exitTrap_jvm, (void**)&env, JNI_VERSION_1_6);
|
||||
if(errorCode == JNI_EDETACHED) {
|
||||
errorCode = (*exitTrap_jvm)->AttachCurrentThread(exitTrap_jvm, &env, NULL);
|
||||
}
|
||||
if(errorCode != JNI_OK) {
|
||||
// Step on a landmine and die, since we can't invoke the Dalvik exit without attaching to
|
||||
// Dalvik.
|
||||
// I mean, if Zygote can do that, why can't I?
|
||||
killpg(getpgrp(), SIGTERM);
|
||||
}
|
||||
if(code != 0) {
|
||||
// Exit code 0 is pretty established as "eh it's fine"
|
||||
// so only open the GUI if the code is != 0
|
||||
(*env)->CallVoidMethod(env, exitTrap_bridge, exitTrap_method, code);
|
||||
}
|
||||
// Delete the reference, not gonna need 'em later anyway
|
||||
(*env)->DeleteGlobalRef(env, exitTrap_bridge);
|
||||
(*exitTrap_jvm)->DetachCurrentThread(exitTrap_jvm);
|
||||
old_exit(code);
|
||||
|
||||
// A hat trick, if you will
|
||||
// Call the Android System.exit() to perform Android's shutdown hooks and do a
|
||||
// fully clean exit.
|
||||
// After doing this, either of these will happen:
|
||||
// 1. Runtime calls exit() for real and it will be handled by ByteHook's recurse handler
|
||||
// and redirected back to the OS
|
||||
// 2. Zygote sends SIGTERM (no handling necessary, the process perishes)
|
||||
// 3. A different thread calls exit() and the hook will go through the exit_tripped path
|
||||
jclass systemClass = (*env)->FindClass(env,"java/lang/System");
|
||||
jmethodID exitMethod = (*env)->GetStaticMethodID(env, systemClass, "exit", "(I)V");
|
||||
(*env)->CallStaticVoidMethod(env, systemClass, exitMethod, 0);
|
||||
// System.exit() should not ever return, but the compiler doesn't know about that
|
||||
// so put a while loop here
|
||||
while(1) {}
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setupExitTrap(JNIEnv *env, jobject jobject1, jobject bridge) {
|
||||
static void custom_exit(int code) {
|
||||
__android_log_print(code == 0 ? ANDROID_LOG_INFO : ANDROID_LOG_ERROR, "FCL", "JVM exit with code %d.", code);
|
||||
// If the exit was already done (meaning it is recursive or from a different thread), pass the call through
|
||||
if(exit_tripped) {
|
||||
BYTEHOOK_CALL_PREV(custom_exit, exit_func, code);
|
||||
BYTEHOOK_POP_STACK();
|
||||
return;
|
||||
}
|
||||
exit_tripped = true;
|
||||
// Perform a nominal exit, as we expect.
|
||||
nominal_exit(code);
|
||||
BYTEHOOK_POP_STACK();
|
||||
}
|
||||
|
||||
static void custom_atexit() {
|
||||
// Same as custom_exit, but without the code or the exit passthrough.
|
||||
if(exit_tripped) {
|
||||
return;
|
||||
}
|
||||
exit_tripped = true;
|
||||
nominal_exit(0);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setupExitTrap(JNIEnv *env, jobject jobject1, jobject bridge) {
|
||||
exitTrap_bridge = (*env)->NewGlobalRef(env, bridge);
|
||||
(*env)->GetJavaVM(env, &exitTrap_jvm);
|
||||
jclass exitTrap_exitClass = (*env)->NewGlobalRef(env,(*env)->FindClass(env, "com/tungsten/fclauncher/bridge/FCLBridge"));
|
||||
exitTrap_method = (*env)->GetMethodID(env, exitTrap_exitClass, "onExit", "(I)V");
|
||||
(*env)->DeleteGlobalRef(env, exitTrap_exitClass);
|
||||
// Enable xhook debug mode here
|
||||
// xhook_enable_debug(1);
|
||||
xhook_register(".*\\.so$", "exit", custom_exit, (void **) &old_exit);
|
||||
return xhook_refresh(1);
|
||||
}
|
||||
|
||||
int
|
||||
(*JLI_Launch)(int argc, char ** argv, /* main argc, argc */
|
||||
int jargc, const char** jargv, /* java args */
|
||||
int appclassc, const char** appclassv, /* app classpath */
|
||||
const char* fullversion, /* full version defined */
|
||||
const char* dotversion, /* dot version defined */
|
||||
const char* pname, /* program name */
|
||||
const char* lname, /* launcher name */
|
||||
jboolean javaargs, /* JAVA_ARGS */
|
||||
jboolean cpwildcard, /* classpath wildcard */
|
||||
jboolean javaw, /* windows-only javaw */
|
||||
jint ergo_class /* ergnomics policy */
|
||||
);
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setupJLI(JNIEnv* env, jobject jobject){
|
||||
|
||||
void* handle;
|
||||
handle = dlopen("libjli.so", RTLD_LAZY | RTLD_GLOBAL);
|
||||
JLI_Launch = (int (*)(int, char **, int, const char**, int, const char**, const char*, const char*, const char*, const char*, jboolean, jboolean, jboolean, jint))dlsym(handle, "JLI_Launch");
|
||||
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_jliLaunch(JNIEnv *env, jobject jobject, jobjectArray argsArray){
|
||||
int argc = (*env)->GetArrayLength(env, argsArray);
|
||||
char* argv[argc];
|
||||
for (int i = 0; i < argc; i++) {
|
||||
jstring str = (*env)->GetObjectArrayElement(env, argsArray, i);
|
||||
int len = (*env)->GetStringUTFLength(env, str);
|
||||
char* buf = malloc(len + 1);
|
||||
int characterLen = (*env)->GetStringLength(env, str);
|
||||
(*env)->GetStringUTFRegion(env, str, 0, characterLen, buf);
|
||||
buf[len] = 0;
|
||||
argv[i] = buf;
|
||||
if(bytehook_init(BYTEHOOK_MODE_AUTOMATIC, false) == BYTEHOOK_STATUS_CODE_OK) {
|
||||
bytehook_hook_all(NULL,
|
||||
"exit",
|
||||
&custom_exit,
|
||||
NULL,
|
||||
NULL);
|
||||
}else {
|
||||
atexit(custom_atexit);
|
||||
}
|
||||
|
||||
return JLI_Launch(argc, argv,
|
||||
sizeof(const_jargs) / sizeof(char *), const_jargs,
|
||||
sizeof(const_appclasspath) / sizeof(char *), const_appclasspath,
|
||||
FULL_VERSION,
|
||||
DOT_VERSION,
|
||||
(const_progname != NULL) ? const_progname : *argv,
|
||||
(const_launcher != NULL) ? const_launcher : *argv,
|
||||
(const_jargs != NULL) ? JNI_TRUE : JNI_FALSE,
|
||||
const_cpwildcard, const_javaw, const_ergo_class);
|
||||
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
#include <android/log.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <jni.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
// Boardwalk: missing include
|
||||
#include <string.h>
|
||||
|
||||
#include "include/fcl_internal.h"
|
||||
|
||||
|
||||
// Uncomment to try redirect signal handling to JVM
|
||||
// #define TRY_SIG2JVM
|
||||
|
||||
// PojavLancher: fixme: are these wrong?
|
||||
#define FULL_VERSION "1.8.0-internal"
|
||||
#define DOT_VERSION "1.8"
|
||||
|
||||
static const char* const_progname = "java";
|
||||
static const char* const_launcher = "openjdk";
|
||||
static const char** const_jargs = NULL;
|
||||
static const char** const_appclasspath = NULL;
|
||||
static const jboolean const_javaw = JNI_FALSE;
|
||||
static const jboolean const_cpwildcard = JNI_TRUE;
|
||||
static const jint const_ergo_class = 0; // DEFAULT_POLICY
|
||||
static struct sigaction old_sa[NSIG];
|
||||
|
||||
void (*__old_sa)(int signal, siginfo_t *info, void *reserved);
|
||||
int (*JVM_handle_linux_signal)(int signo, siginfo_t* siginfo, void* ucontext, int abort_if_unrecognized);
|
||||
|
||||
void android_sigaction(int signal, siginfo_t *info, void *reserved) {
|
||||
printf("process killed with signal %d code %p addr %p\n", signal,info->si_code,info->si_addr);
|
||||
if (JVM_handle_linux_signal == NULL) { // should not happen, but still
|
||||
__old_sa = old_sa[signal].sa_sigaction;
|
||||
__old_sa(signal,info,reserved);
|
||||
exit(1);
|
||||
} else {
|
||||
// Based on https://github.com/PojavLauncherTeam/openjdk-multiarch-jdk8u/blob/aarch64-shenandoah-jdk8u272-b10/hotspot/src/os/linux/vm/os_linux.cpp#L4688-4693
|
||||
int orig_errno = errno; // Preserve errno value over signal handler.
|
||||
JVM_handle_linux_signal(signal, info, reserved, true);
|
||||
errno = orig_errno;
|
||||
}
|
||||
}
|
||||
typedef jint JNI_CreateJavaVM_func(JavaVM **pvm, void **penv, void *args);
|
||||
|
||||
typedef jint JLI_Launch_func(int argc, char ** argv, /* main argc, argc */
|
||||
int jargc, const char** jargv, /* java args */
|
||||
int appclassc, const char** appclassv, /* app classpath */
|
||||
const char* fullversion, /* full version defined */
|
||||
const char* dotversion, /* dot version defined */
|
||||
const char* pname, /* program name */
|
||||
const char* lname, /* launcher name */
|
||||
jboolean javaargs, /* JAVA_ARGS */
|
||||
jboolean cpwildcard, /* classpath wildcard*/
|
||||
jboolean javaw, /* windows-only javaw */
|
||||
jint ergo /* ergonomics class policy */
|
||||
);
|
||||
|
||||
static jint launchJVM(int margc, char** margv) {
|
||||
void* libjli = dlopen("libjli.so", RTLD_LAZY | RTLD_GLOBAL);
|
||||
|
||||
// Boardwalk: silence
|
||||
// LOGD("JLI lib = %x", (int)libjli);
|
||||
if (NULL == libjli) {
|
||||
FCL_INTERNAL_LOG("JLI lib = NULL: %s", dlerror());
|
||||
return -1;
|
||||
}
|
||||
FCL_INTERNAL_LOG("Found JLI lib");
|
||||
|
||||
JLI_Launch_func *pJLI_Launch =
|
||||
(JLI_Launch_func *)dlsym(libjli, "JLI_Launch");
|
||||
// Boardwalk: silence
|
||||
// LOGD("JLI_Launch = 0x%x", *(int*)&pJLI_Launch);
|
||||
|
||||
if (NULL == pJLI_Launch) {
|
||||
FCL_INTERNAL_LOG("JLI_Launch = NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
FCL_INTERNAL_LOG("Calling JLI_Launch");
|
||||
|
||||
return pJLI_Launch(margc, margv,
|
||||
0, NULL, // sizeof(const_jargs) / sizeof(char *), const_jargs,
|
||||
0, NULL, // sizeof(const_appclasspath) / sizeof(char *), const_appclasspath,
|
||||
FULL_VERSION,
|
||||
DOT_VERSION,
|
||||
*margv, // (const_progname != NULL) ? const_progname : *margv,
|
||||
*margv, // (const_launcher != NULL) ? const_launcher : *margv,
|
||||
(const_jargs != NULL) ? JNI_TRUE : JNI_FALSE,
|
||||
const_cpwildcard, const_javaw, const_ergo_class);
|
||||
/*
|
||||
return pJLI_Launch(argc, argv,
|
||||
0, NULL, 0, NULL, FULL_VERSION,
|
||||
DOT_VERSION, *margv, *margv, // "java", "openjdk",
|
||||
JNI_FALSE, JNI_TRUE, JNI_FALSE, 0);
|
||||
*/
|
||||
}
|
||||
|
||||
char** convert_to_char_array(JNIEnv *env, jobjectArray jstringArray) {
|
||||
int num_rows = (*env)->GetArrayLength(env, jstringArray);
|
||||
char **cArray = (char **) malloc(num_rows * sizeof(char*));
|
||||
jstring row;
|
||||
|
||||
for (int i = 0; i < num_rows; i++) {
|
||||
row = (jstring) (*env)->GetObjectArrayElement(env, jstringArray, i);
|
||||
cArray[i] = (char*)(*env)->GetStringUTFChars(env, row, 0);
|
||||
}
|
||||
|
||||
return cArray;
|
||||
}
|
||||
|
||||
|
||||
void free_char_array(JNIEnv *env, jobjectArray jstringArray, const char **charArray) {
|
||||
int num_rows = (*env)->GetArrayLength(env, jstringArray);
|
||||
jstring row;
|
||||
|
||||
for (int i = 0; i < num_rows; i++) {
|
||||
row = (jstring) (*env)->GetObjectArrayElement(env, jstringArray, i);
|
||||
(*env)->ReleaseStringUTFChars(env, row, charArray[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: com_oracle_dalvik_VMLauncher
|
||||
* Method: launchJVM
|
||||
* Signature: ([Ljava/lang/String;)I
|
||||
*/
|
||||
|
||||
jint JNICALL Java_com_oracle_dalvik_VMLauncher_launchJVM(JNIEnv *env, jclass clazz, jobjectArray argsArray) {
|
||||
#ifdef TRY_SIG2JVM
|
||||
void* libjvm = dlopen("libjvm.so", RTLD_LAZY | RTLD_GLOBAL);
|
||||
if (NULL == libjvm) {
|
||||
LOGE("JVM lib = NULL: %s", dlerror());
|
||||
return -1;
|
||||
}
|
||||
JVM_handle_linux_signal = dlsym(libjvm, "JVM_handle_linux_signal");
|
||||
#endif
|
||||
|
||||
jint res = 0;
|
||||
// int i;
|
||||
//Prepare the signal trapper
|
||||
struct sigaction catcher;
|
||||
memset(&catcher,0,sizeof(sigaction));
|
||||
catcher.sa_sigaction = android_sigaction;
|
||||
catcher.sa_flags = SA_SIGINFO|SA_RESTART;
|
||||
// SA_RESETHAND;
|
||||
#define CATCHSIG(X) sigaction(X, &catcher, &old_sa[X])
|
||||
CATCHSIG(SIGILL);
|
||||
//CATCHSIG(SIGABRT);
|
||||
CATCHSIG(SIGBUS);
|
||||
CATCHSIG(SIGFPE);
|
||||
#ifdef TRY_SIG2JVM
|
||||
CATCHSIG(SIGSEGV);
|
||||
#endif
|
||||
CATCHSIG(SIGSTKFLT);
|
||||
CATCHSIG(SIGPIPE);
|
||||
CATCHSIG(SIGXFSZ);
|
||||
//Signal trapper ready
|
||||
|
||||
// Save dalvik JNIEnv pointer for JVM launch thread
|
||||
// pojav_environ->dalvikJNIEnvPtr_ANDROID = env;
|
||||
|
||||
if (argsArray == NULL) {
|
||||
FCL_INTERNAL_LOG("Args array null, returning");
|
||||
//handle error
|
||||
return 0;
|
||||
}
|
||||
|
||||
int argc = (*env)->GetArrayLength(env, argsArray);
|
||||
char **argv = convert_to_char_array(env, argsArray);
|
||||
|
||||
FCL_INTERNAL_LOG("Done processing args");
|
||||
|
||||
res = launchJVM(argc, argv);
|
||||
|
||||
FCL_INTERNAL_LOG("Going to free args");
|
||||
free_char_array(env, argsArray, argv);
|
||||
|
||||
FCL_INTERNAL_LOG("Free done");
|
||||
|
||||
return res;
|
||||
}
|
|
@ -1,554 +0,0 @@
|
|||
/*-
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
* $FreeBSD: stable/9/sys/sys/queue.h 252365 2013-06-29 04:25:40Z lstewart $
|
||||
*/
|
||||
|
||||
#ifndef QUEUE_H
|
||||
#define QUEUE_H
|
||||
|
||||
/* #include <sys/cdefs.h> */
|
||||
#define __containerof(ptr, type, field) ((type *)((char *)(ptr) - ((char *)&((type *)0)->field)))
|
||||
|
||||
/*
|
||||
* This file defines four types of data structures: singly-linked lists,
|
||||
* singly-linked tail queues, lists and tail queues.
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A singly-linked tail queue is headed by a pair of pointers, one to the
|
||||
* head of the list and the other to the tail of the list. The elements are
|
||||
* singly linked for minimum space and pointer manipulation overhead at the
|
||||
* expense of O(n) removal for arbitrary elements. New elements can be added
|
||||
* to the list after an existing element, at the head of the list, or at the
|
||||
* end of the list. Elements being removed from the head of the tail queue
|
||||
* should use the explicit macro for this purpose for optimum efficiency.
|
||||
* A singly-linked tail queue may only be traversed in the forward direction.
|
||||
* Singly-linked tail queues are ideal for applications with large datasets
|
||||
* and few or no removals or for implementing a FIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may be traversed in either direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*
|
||||
* SLIST LIST STAILQ TAILQ
|
||||
* _HEAD + + + +
|
||||
* _HEAD_INITIALIZER + + + +
|
||||
* _ENTRY + + + +
|
||||
* _INIT + + + +
|
||||
* _EMPTY + + + +
|
||||
* _FIRST + + + +
|
||||
* _NEXT + + + +
|
||||
* _PREV - + - +
|
||||
* _LAST - - + +
|
||||
* _FOREACH + + + +
|
||||
* _FOREACH_FROM + + + +
|
||||
* _FOREACH_SAFE + + + +
|
||||
* _FOREACH_FROM_SAFE + + + +
|
||||
* _FOREACH_REVERSE - - - +
|
||||
* _FOREACH_REVERSE_FROM - - - +
|
||||
* _FOREACH_REVERSE_SAFE - - - +
|
||||
* _FOREACH_REVERSE_FROM_SAFE - - - +
|
||||
* _INSERT_HEAD + + + +
|
||||
* _INSERT_BEFORE - + - +
|
||||
* _INSERT_AFTER + + + +
|
||||
* _INSERT_TAIL - - + +
|
||||
* _CONCAT - - + +
|
||||
* _REMOVE_AFTER + - + -
|
||||
* _REMOVE_HEAD + - + -
|
||||
* _REMOVE + + + +
|
||||
* _SWAP + + + +
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Singly-linked List declarations.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type, qual) \
|
||||
struct name { \
|
||||
struct type *qual slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type, qual) \
|
||||
struct { \
|
||||
struct type *qual sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_INIT(head) do { \
|
||||
SLIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
|
||||
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_FOREACH_FROM(var, head, field) \
|
||||
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
|
||||
(var); \
|
||||
(var) = SLIST_NEXT((var), field))
|
||||
|
||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SLIST_FIRST((head)); \
|
||||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
|
||||
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
|
||||
SLIST_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
|
||||
SLIST_NEXT((slistelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_AFTER(elm, field) do { \
|
||||
SLIST_NEXT(elm, field) = \
|
||||
SLIST_NEXT(SLIST_NEXT(elm, field), field); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
if (SLIST_FIRST((head)) == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = SLIST_FIRST((head)); \
|
||||
while (SLIST_NEXT(curelm, field) != (elm)) \
|
||||
curelm = SLIST_NEXT(curelm, field); \
|
||||
SLIST_REMOVE_AFTER(curelm, field); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_SWAP(head1, head2, type) do { \
|
||||
struct type *swap_first = SLIST_FIRST(head1); \
|
||||
SLIST_FIRST(head1) = SLIST_FIRST(head2); \
|
||||
SLIST_FIRST(head2) = swap_first; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* List declarations.
|
||||
*/
|
||||
#define LIST_HEAD(name, type, qual) \
|
||||
struct name { \
|
||||
struct type *qual lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type, qual) \
|
||||
struct { \
|
||||
struct type *qual le_next; /* next element */ \
|
||||
struct type *qual *le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST((head)) = NULL; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
|
||||
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_PREV(elm, head, type, field) \
|
||||
((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
|
||||
__containerof((elm)->field.le_prev, struct type, field.le_next))
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = LIST_NEXT((var), field))
|
||||
|
||||
#define LIST_FOREACH_FROM(var, head, field) \
|
||||
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
|
||||
(var); \
|
||||
(var) = LIST_NEXT((var), field))
|
||||
|
||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = LIST_FIRST((head)); \
|
||||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
|
||||
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
|
||||
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field); \
|
||||
LIST_FIRST((head)) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
LIST_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL) \
|
||||
LIST_NEXT((listelm), field)->field.le_prev = \
|
||||
&LIST_NEXT((elm), field); \
|
||||
LIST_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
if (LIST_NEXT((elm), field) != NULL) \
|
||||
LIST_NEXT((elm), field)->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_SWAP(head1, head2, type, field) do { \
|
||||
struct type *swap_tmp = LIST_FIRST((head1)); \
|
||||
LIST_FIRST((head1)) = LIST_FIRST((head2)); \
|
||||
LIST_FIRST((head2)) = swap_tmp; \
|
||||
if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
|
||||
swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
|
||||
if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
|
||||
swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue declarations.
|
||||
*/
|
||||
#define STAILQ_HEAD(name, type, qual) \
|
||||
struct name { \
|
||||
struct type *qual stqh_first;/* first element */ \
|
||||
struct type *qual *stqh_last;/* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define STAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).stqh_first }
|
||||
|
||||
#define STAILQ_ENTRY(type, qual) \
|
||||
struct { \
|
||||
struct type *qual stqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked Tail queue functions.
|
||||
*/
|
||||
#define STAILQ_INIT(head) do { \
|
||||
STAILQ_FIRST((head)) = NULL; \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
|
||||
|
||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
||||
|
||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
||||
|
||||
#define STAILQ_LAST(head, type, field) \
|
||||
(STAILQ_EMPTY((head)) ? NULL : \
|
||||
__containerof((head)->stqh_last, struct type, field.stqe_next))
|
||||
|
||||
#define STAILQ_FOREACH(var, head, field) \
|
||||
for((var) = STAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = STAILQ_NEXT((var), field))
|
||||
|
||||
#define STAILQ_FOREACH_FROM(var, head, field) \
|
||||
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
|
||||
(var); \
|
||||
(var) = STAILQ_NEXT((var), field))
|
||||
|
||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = STAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
|
||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_FIRST((head)) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
|
||||
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
STAILQ_NEXT((tqelm), field) = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
STAILQ_NEXT((elm), field) = NULL; \
|
||||
*(head)->stqh_last = (elm); \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_CONCAT(head1, head2) do { \
|
||||
if (!STAILQ_EMPTY((head2))) { \
|
||||
*(head1)->stqh_last = (head2)->stqh_first; \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if ((STAILQ_NEXT(elm, field) = \
|
||||
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
||||
if ((STAILQ_FIRST((head)) = \
|
||||
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
|
||||
(head)->stqh_last = &STAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
||||
if (STAILQ_FIRST((head)) == (elm)) { \
|
||||
STAILQ_REMOVE_HEAD((head), field); \
|
||||
} \
|
||||
else { \
|
||||
struct type *curelm = STAILQ_FIRST((head)); \
|
||||
while (STAILQ_NEXT(curelm, field) != (elm)) \
|
||||
curelm = STAILQ_NEXT(curelm, field); \
|
||||
STAILQ_REMOVE_AFTER(head, curelm, field); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define STAILQ_SWAP(head1, head2, type) do { \
|
||||
struct type *swap_first = STAILQ_FIRST(head1); \
|
||||
struct type **swap_last = (head1)->stqh_last; \
|
||||
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
|
||||
(head1)->stqh_last = (head2)->stqh_last; \
|
||||
STAILQ_FIRST(head2) = swap_first; \
|
||||
(head2)->stqh_last = swap_last; \
|
||||
if (STAILQ_EMPTY(head1)) \
|
||||
(head1)->stqh_last = &STAILQ_FIRST(head1); \
|
||||
if (STAILQ_EMPTY(head2)) \
|
||||
(head2)->stqh_last = &STAILQ_FIRST(head2); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Tail queue declarations.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type, qual) \
|
||||
struct name { \
|
||||
struct type *qual tqh_first; /* first element */ \
|
||||
struct type *qual *tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define TAILQ_ENTRY(type, qual) \
|
||||
struct { \
|
||||
struct type *qual tqe_next; /* next element */ \
|
||||
struct type *qual *tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_INIT(head) do { \
|
||||
TAILQ_FIRST((head)) = NULL; \
|
||||
(head)->tqh_last = &TAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
|
||||
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var); \
|
||||
(var) = TAILQ_NEXT((var), field))
|
||||
|
||||
#define TAILQ_FOREACH_FROM(var, head, field) \
|
||||
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
|
||||
(var); \
|
||||
(var) = TAILQ_NEXT((var), field))
|
||||
|
||||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = TAILQ_FIRST((head)); \
|
||||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
|
||||
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var); \
|
||||
(var) = TAILQ_PREV((var), headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
|
||||
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
|
||||
(var); \
|
||||
(var) = TAILQ_PREV((var), headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = TAILQ_LAST((head), headname); \
|
||||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
|
||||
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
|
||||
TAILQ_FIRST((head))->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
TAILQ_FIRST((head)) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
TAILQ_NEXT((elm), field) = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL) \
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
&TAILQ_NEXT((elm), field); \
|
||||
else \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
TAILQ_NEXT((listelm), field) = (elm); \
|
||||
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
TAILQ_NEXT((elm), field) = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
||||
if (!TAILQ_EMPTY(head2)) { \
|
||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
TAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
if ((TAILQ_NEXT((elm), field)) != NULL) \
|
||||
TAILQ_NEXT((elm), field)->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_SWAP(head1, head2, type, field) do { \
|
||||
struct type *swap_first = (head1)->tqh_first; \
|
||||
struct type **swap_last = (head1)->tqh_last; \
|
||||
(head1)->tqh_first = (head2)->tqh_first; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
(head2)->tqh_first = swap_first; \
|
||||
(head2)->tqh_last = swap_last; \
|
||||
if ((swap_first = (head1)->tqh_first) != NULL) \
|
||||
swap_first->field.tqe_prev = &(head1)->tqh_first; \
|
||||
else \
|
||||
(head1)->tqh_last = &(head1)->tqh_first; \
|
||||
if ((swap_first = (head2)->tqh_first) != NULL) \
|
||||
swap_first->field.tqe_prev = &(head2)->tqh_first; \
|
||||
else \
|
||||
(head2)->tqh_last = &(head2)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#endif
|
|
@ -1,768 +0,0 @@
|
|||
/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
|
||||
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
|
||||
/* $FreeBSD: stable/9/sys/sys/tree.h 189204 2009-03-01 04:57:23Z bms $ */
|
||||
|
||||
/*-
|
||||
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef TREE_H
|
||||
#define TREE_H
|
||||
|
||||
/* #include <sys/cdefs.h> */
|
||||
#ifndef __unused
|
||||
#define __unused __attribute__((__unused__))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This file defines data structures for different types of trees:
|
||||
* splay trees and red-black trees.
|
||||
*
|
||||
* A splay tree is a self-organizing data structure. Every operation
|
||||
* on the tree causes a splay to happen. The splay moves the requested
|
||||
* node to the root of the tree and partly rebalances it.
|
||||
*
|
||||
* This has the benefit that request locality causes faster lookups as
|
||||
* the requested nodes move to the top of the tree. On the other hand,
|
||||
* every lookup causes memory writes.
|
||||
*
|
||||
* The Balance Theorem bounds the total access time for m operations
|
||||
* and n inserts on an initially empty tree as O((m + n)lg n). The
|
||||
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
|
||||
*
|
||||
* A red-black tree is a binary search tree with the node color as an
|
||||
* extra attribute. It fulfills a set of conditions:
|
||||
* - every search path from the root to a leaf consists of the
|
||||
* same number of black nodes,
|
||||
* - each red node (except for the root) has a black parent,
|
||||
* - each leaf node is black.
|
||||
*
|
||||
* Every operation on a red-black tree is bounded as O(lg n).
|
||||
* The maximum height of a red-black tree is 2lg (n+1).
|
||||
*/
|
||||
|
||||
#define SPLAY_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sph_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define SPLAY_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define SPLAY_INIT(root) do { \
|
||||
(root)->sph_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *spe_left; /* left element */ \
|
||||
struct type *spe_right; /* right element */ \
|
||||
}
|
||||
|
||||
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
|
||||
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
|
||||
#define SPLAY_ROOT(head) (head)->sph_root
|
||||
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
|
||||
|
||||
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
|
||||
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKLEFT(head, tmp, field) do { \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
|
||||
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
|
||||
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
|
||||
void name##_SPLAY(struct name *, struct type *); \
|
||||
void name##_SPLAY_MINMAX(struct name *, int); \
|
||||
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
|
||||
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return(NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) \
|
||||
return (head->sph_root); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
name##_SPLAY(head, elm); \
|
||||
if (SPLAY_RIGHT(elm, field) != NULL) { \
|
||||
elm = SPLAY_RIGHT(elm, field); \
|
||||
while (SPLAY_LEFT(elm, field) != NULL) { \
|
||||
elm = SPLAY_LEFT(elm, field); \
|
||||
} \
|
||||
} else \
|
||||
elm = NULL; \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_MIN_MAX(struct name *head, int val) \
|
||||
{ \
|
||||
name##_SPLAY_MINMAX(head, val); \
|
||||
return (SPLAY_ROOT(head)); \
|
||||
}
|
||||
|
||||
/* Main splay operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define SPLAY_GENERATE(name, type, field, cmp) \
|
||||
struct type * \
|
||||
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
|
||||
} else { \
|
||||
int __comp; \
|
||||
name##_SPLAY(head, elm); \
|
||||
__comp = (cmp)(elm, (head)->sph_root); \
|
||||
if(__comp < 0) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
|
||||
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_LEFT((head)->sph_root, field) = NULL; \
|
||||
} else if (__comp > 0) { \
|
||||
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
SPLAY_LEFT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
|
||||
} else \
|
||||
return ((head)->sph_root); \
|
||||
} \
|
||||
(head)->sph_root = (elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
struct type * \
|
||||
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *__tmp; \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return (NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) { \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
|
||||
} else { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
|
||||
name##_SPLAY(head, elm); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
void \
|
||||
name##_SPLAY(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
int __comp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) > 0){ \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
} \
|
||||
\
|
||||
/* Splay with either the minimum or the maximum element \
|
||||
* Used to find minimum or maximum element in tree. \
|
||||
*/ \
|
||||
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while (1) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp > 0) { \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
}
|
||||
|
||||
#define SPLAY_NEGINF -1
|
||||
#define SPLAY_INF 1
|
||||
|
||||
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
|
||||
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
|
||||
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
|
||||
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
|
||||
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
|
||||
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
|
||||
|
||||
#define SPLAY_FOREACH(x, name, head) \
|
||||
for ((x) = SPLAY_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = SPLAY_NEXT(name, head, x))
|
||||
|
||||
/* Macros that define a red-black tree */
|
||||
#define RB_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *rbh_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define RB_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define RB_INIT(root) do { \
|
||||
(root)->rbh_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_BLACK 0
|
||||
#define RB_RED 1
|
||||
#define RB_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *rbe_left; /* left element */ \
|
||||
struct type *rbe_right; /* right element */ \
|
||||
struct type *rbe_parent; /* parent element */ \
|
||||
int rbe_color; /* node color */ \
|
||||
}
|
||||
|
||||
#define RB_LEFT(elm, field) (elm)->field.rbe_left
|
||||
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
|
||||
#define RB_PARENT(elm, field) (elm)->field.rbe_parent
|
||||
#define RB_COLOR(elm, field) (elm)->field.rbe_color
|
||||
#define RB_ROOT(head) (head)->rbh_root
|
||||
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
|
||||
|
||||
#define RB_SET(elm, parent, field) do { \
|
||||
RB_PARENT(elm, field) = parent; \
|
||||
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
|
||||
RB_COLOR(elm, field) = RB_RED; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_SET_BLACKRED(black, red, field) do { \
|
||||
RB_COLOR(black, field) = RB_BLACK; \
|
||||
RB_COLOR(red, field) = RB_RED; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#ifndef RB_AUGMENT
|
||||
#define RB_AUGMENT(x) do {} while (0)
|
||||
#endif
|
||||
|
||||
#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_RIGHT(elm, field); \
|
||||
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
|
||||
RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_LEFT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \
|
||||
(tmp) = RB_LEFT(elm, field); \
|
||||
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
|
||||
RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \
|
||||
} \
|
||||
RB_AUGMENT(elm); \
|
||||
if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \
|
||||
if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \
|
||||
RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \
|
||||
} else \
|
||||
(head)->rbh_root = (tmp); \
|
||||
RB_RIGHT(tmp, field) = (elm); \
|
||||
RB_PARENT(elm, field) = (tmp); \
|
||||
RB_AUGMENT(tmp); \
|
||||
if ((RB_PARENT(tmp, field))) \
|
||||
RB_AUGMENT(RB_PARENT(tmp, field)); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
#define RB_PROTOTYPE(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
|
||||
#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
|
||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
|
||||
#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
|
||||
attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \
|
||||
attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
|
||||
attr struct type *name##_RB_REMOVE(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_INSERT(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_FIND(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_NFIND(struct name *, struct type *); \
|
||||
attr struct type *name##_RB_NEXT(struct type *); \
|
||||
attr struct type *name##_RB_PREV(struct type *); \
|
||||
attr struct type *name##_RB_MINMAX(struct name *, int); \
|
||||
\
|
||||
|
||||
/* Main rb operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define RB_GENERATE(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp,)
|
||||
#define RB_GENERATE_STATIC(name, type, field, cmp) \
|
||||
RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
|
||||
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
|
||||
attr void \
|
||||
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *parent, *gparent, *tmp; \
|
||||
while ((parent = RB_PARENT(elm, field)) != NULL && \
|
||||
RB_COLOR(parent, field) == RB_RED) { \
|
||||
gparent = RB_PARENT(parent, field); \
|
||||
if (parent == RB_LEFT(gparent, field)) { \
|
||||
tmp = RB_RIGHT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field);\
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_RIGHT(parent, field) == elm) { \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_RIGHT(head, gparent, tmp, field); \
|
||||
} else { \
|
||||
tmp = RB_LEFT(gparent, field); \
|
||||
if (tmp && RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_COLOR(tmp, field) = RB_BLACK; \
|
||||
RB_SET_BLACKRED(parent, gparent, field);\
|
||||
elm = gparent; \
|
||||
continue; \
|
||||
} \
|
||||
if (RB_LEFT(parent, field) == elm) { \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
tmp = parent; \
|
||||
parent = elm; \
|
||||
elm = tmp; \
|
||||
} \
|
||||
RB_SET_BLACKRED(parent, gparent, field); \
|
||||
RB_ROTATE_LEFT(head, gparent, tmp, field); \
|
||||
} \
|
||||
} \
|
||||
RB_COLOR(head->rbh_root, field) = RB_BLACK; \
|
||||
} \
|
||||
\
|
||||
attr void \
|
||||
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \
|
||||
elm != RB_ROOT(head)) { \
|
||||
if (RB_LEFT(parent, field) == elm) { \
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} else { \
|
||||
if (RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\
|
||||
struct type *oleft; \
|
||||
if ((oleft = RB_LEFT(tmp, field)) \
|
||||
!= NULL) \
|
||||
RB_COLOR(oleft, field) = RB_BLACK;\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_RIGHT(head, tmp, oleft, field);\
|
||||
tmp = RB_RIGHT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_RIGHT(tmp, field)) \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\
|
||||
RB_ROTATE_LEFT(head, parent, tmp, field);\
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} else { \
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
if (RB_COLOR(tmp, field) == RB_RED) { \
|
||||
RB_SET_BLACKRED(tmp, parent, field); \
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
if ((RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\
|
||||
(RB_RIGHT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
elm = parent; \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
} else { \
|
||||
if (RB_LEFT(tmp, field) == NULL || \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\
|
||||
struct type *oright; \
|
||||
if ((oright = RB_RIGHT(tmp, field)) \
|
||||
!= NULL) \
|
||||
RB_COLOR(oright, field) = RB_BLACK;\
|
||||
RB_COLOR(tmp, field) = RB_RED; \
|
||||
RB_ROTATE_LEFT(head, tmp, oright, field);\
|
||||
tmp = RB_LEFT(parent, field); \
|
||||
} \
|
||||
RB_COLOR(tmp, field) = RB_COLOR(parent, field);\
|
||||
RB_COLOR(parent, field) = RB_BLACK; \
|
||||
if (RB_LEFT(tmp, field)) \
|
||||
RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\
|
||||
RB_ROTATE_RIGHT(head, parent, tmp, field);\
|
||||
elm = RB_ROOT(head); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (elm) \
|
||||
RB_COLOR(elm, field) = RB_BLACK; \
|
||||
} \
|
||||
\
|
||||
attr struct type * \
|
||||
name##_RB_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *child, *parent, *old = elm; \
|
||||
int color; \
|
||||
if (RB_LEFT(elm, field) == NULL) \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
else if (RB_RIGHT(elm, field) == NULL) \
|
||||
child = RB_LEFT(elm, field); \
|
||||
else { \
|
||||
struct type *left; \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while ((left = RB_LEFT(elm, field)) != NULL) \
|
||||
elm = left; \
|
||||
child = RB_RIGHT(elm, field); \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) { \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = child; \
|
||||
if (RB_PARENT(elm, field) == old) \
|
||||
parent = elm; \
|
||||
(elm)->field = (old)->field; \
|
||||
if (RB_PARENT(old, field)) { \
|
||||
if (RB_LEFT(RB_PARENT(old, field), field) == old)\
|
||||
RB_LEFT(RB_PARENT(old, field), field) = elm;\
|
||||
else \
|
||||
RB_RIGHT(RB_PARENT(old, field), field) = elm;\
|
||||
RB_AUGMENT(RB_PARENT(old, field)); \
|
||||
} else \
|
||||
RB_ROOT(head) = elm; \
|
||||
RB_PARENT(RB_LEFT(old, field), field) = elm; \
|
||||
if (RB_RIGHT(old, field)) \
|
||||
RB_PARENT(RB_RIGHT(old, field), field) = elm; \
|
||||
if (parent) { \
|
||||
left = parent; \
|
||||
do { \
|
||||
RB_AUGMENT(left); \
|
||||
} while ((left = RB_PARENT(left, field)) != NULL); \
|
||||
} \
|
||||
goto color; \
|
||||
} \
|
||||
parent = RB_PARENT(elm, field); \
|
||||
color = RB_COLOR(elm, field); \
|
||||
if (child) \
|
||||
RB_PARENT(child, field) = parent; \
|
||||
if (parent) { \
|
||||
if (RB_LEFT(parent, field) == elm) \
|
||||
RB_LEFT(parent, field) = child; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = child; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = child; \
|
||||
color: \
|
||||
if (color == RB_BLACK) \
|
||||
name##_RB_REMOVE_COLOR(head, parent, child); \
|
||||
return (old); \
|
||||
} \
|
||||
\
|
||||
/* Inserts a node into the RB tree */ \
|
||||
attr struct type * \
|
||||
name##_RB_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp; \
|
||||
struct type *parent = NULL; \
|
||||
int comp = 0; \
|
||||
tmp = RB_ROOT(head); \
|
||||
while (tmp) { \
|
||||
parent = tmp; \
|
||||
comp = (cmp)(elm, parent); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
RB_SET(elm, parent, field); \
|
||||
if (parent != NULL) { \
|
||||
if (comp < 0) \
|
||||
RB_LEFT(parent, field) = elm; \
|
||||
else \
|
||||
RB_RIGHT(parent, field) = elm; \
|
||||
RB_AUGMENT(parent); \
|
||||
} else \
|
||||
RB_ROOT(head) = elm; \
|
||||
name##_RB_INSERT_COLOR(head, elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
attr struct type * \
|
||||
name##_RB_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
int comp; \
|
||||
while (tmp) { \
|
||||
comp = cmp(elm, tmp); \
|
||||
if (comp < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
/* Finds the first node greater than or equal to the search key */ \
|
||||
attr struct type * \
|
||||
name##_RB_NFIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
struct type *res = NULL; \
|
||||
int comp; \
|
||||
while (tmp) { \
|
||||
comp = cmp(elm, tmp); \
|
||||
if (comp < 0) { \
|
||||
res = tmp; \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
} \
|
||||
else if (comp > 0) \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
else \
|
||||
return (tmp); \
|
||||
} \
|
||||
return (res); \
|
||||
} \
|
||||
\
|
||||
/* ARGSUSED */ \
|
||||
attr struct type * \
|
||||
name##_RB_NEXT(struct type *elm) \
|
||||
{ \
|
||||
if (RB_RIGHT(elm, field)) { \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
while (RB_LEFT(elm, field)) \
|
||||
elm = RB_LEFT(elm, field); \
|
||||
} else { \
|
||||
if (RB_PARENT(elm, field) && \
|
||||
(elm == RB_LEFT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
else { \
|
||||
while (RB_PARENT(elm, field) && \
|
||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field)))\
|
||||
elm = RB_PARENT(elm, field); \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
/* ARGSUSED */ \
|
||||
attr struct type * \
|
||||
name##_RB_PREV(struct type *elm) \
|
||||
{ \
|
||||
if (RB_LEFT(elm, field)) { \
|
||||
elm = RB_LEFT(elm, field); \
|
||||
while (RB_RIGHT(elm, field)) \
|
||||
elm = RB_RIGHT(elm, field); \
|
||||
} else { \
|
||||
if (RB_PARENT(elm, field) && \
|
||||
(elm == RB_RIGHT(RB_PARENT(elm, field), field))) \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
else { \
|
||||
while (RB_PARENT(elm, field) && \
|
||||
(elm == RB_LEFT(RB_PARENT(elm, field), field)))\
|
||||
elm = RB_PARENT(elm, field); \
|
||||
elm = RB_PARENT(elm, field); \
|
||||
} \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
attr struct type * \
|
||||
name##_RB_MINMAX(struct name *head, int val) \
|
||||
{ \
|
||||
struct type *tmp = RB_ROOT(head); \
|
||||
struct type *parent = NULL; \
|
||||
while (tmp) { \
|
||||
parent = tmp; \
|
||||
if (val < 0) \
|
||||
tmp = RB_LEFT(tmp, field); \
|
||||
else \
|
||||
tmp = RB_RIGHT(tmp, field); \
|
||||
} \
|
||||
return (parent); \
|
||||
}
|
||||
|
||||
#define RB_NEGINF -1
|
||||
#define RB_INF 1
|
||||
|
||||
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
|
||||
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
|
||||
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
|
||||
#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
|
||||
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
|
||||
#define RB_PREV(name, x, y) name##_RB_PREV(y)
|
||||
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
|
||||
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
|
||||
|
||||
#define RB_FOREACH(x, name, head) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_NEXT(x))
|
||||
|
||||
#define RB_FOREACH_FROM(x, name, y) \
|
||||
for ((x) = (y); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MIN(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_REVERSE(x, name, head) \
|
||||
for ((x) = RB_MAX(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = name##_RB_PREV(x))
|
||||
|
||||
#define RB_FOREACH_REVERSE_FROM(x, name, y) \
|
||||
for ((x) = (y); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \
|
||||
for ((x) = RB_MAX(name, head); \
|
||||
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
|
||||
(x) = (y))
|
||||
|
||||
#endif
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// Created by caikelun on 2018-04-11.
|
||||
|
||||
#ifndef XH_CORE_H
|
||||
#define XH_CORE_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int xh_core_register(const char *pathname_regex_str, const char *symbol,
|
||||
void *new_func, void **old_func);
|
||||
|
||||
int xh_core_ignore(const char *pathname_regex_str, const char *symbol);
|
||||
|
||||
int xh_core_refresh(int async);
|
||||
|
||||
void xh_core_clear();
|
||||
|
||||
void xh_core_enable_debug(int flag);
|
||||
|
||||
void xh_core_enable_sigsegv_protection(int flag);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,85 +0,0 @@
|
|||
// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// Created by caikelun on 2018-04-11.
|
||||
|
||||
#ifndef XH_ELF_H
|
||||
#define XH_ELF_H 1
|
||||
|
||||
#include <stdint.h>
|
||||
#include <elf.h>
|
||||
#include <link.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *pathname;
|
||||
|
||||
ElfW(Addr) base_addr;
|
||||
ElfW(Addr) bias_addr;
|
||||
|
||||
ElfW(Ehdr) *ehdr;
|
||||
ElfW(Phdr) *phdr;
|
||||
|
||||
ElfW(Dyn) *dyn; //.dynamic
|
||||
ElfW(Word) dyn_sz;
|
||||
|
||||
const char *strtab; //.dynstr (string-table)
|
||||
ElfW(Sym) *symtab; //.dynsym (symbol-index to string-table's offset)
|
||||
|
||||
ElfW(Addr) relplt; //.rel.plt or .rela.plt
|
||||
ElfW(Word) relplt_sz;
|
||||
|
||||
ElfW(Addr) reldyn; //.rel.dyn or .rela.dyn
|
||||
ElfW(Word) reldyn_sz;
|
||||
|
||||
ElfW(Addr) relandroid; //android compressed rel or rela
|
||||
ElfW(Word) relandroid_sz;
|
||||
|
||||
//for ELF hash
|
||||
uint32_t *bucket;
|
||||
uint32_t bucket_cnt;
|
||||
uint32_t *chain;
|
||||
uint32_t chain_cnt; //invalid for GNU hash
|
||||
|
||||
//append for GNU hash
|
||||
uint32_t symoffset;
|
||||
ElfW(Addr) *bloom;
|
||||
uint32_t bloom_sz;
|
||||
uint32_t bloom_shift;
|
||||
|
||||
int is_use_rela;
|
||||
int is_use_gnu_hash;
|
||||
} xh_elf_t;
|
||||
|
||||
int xh_elf_init(xh_elf_t *self, uintptr_t base_addr, const char *pathname);
|
||||
int xh_elf_hook(xh_elf_t *self, const char *symbol, void *new_func, void **old_func);
|
||||
|
||||
int xh_elf_check_elfheader(uintptr_t base_addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// Created by caikelun on 2018-04-11.
|
||||
|
||||
#ifndef XH_ERRNO_H
|
||||
#define XH_ERRNO_H 1
|
||||
|
||||
#define XH_ERRNO_UNKNOWN 1001
|
||||
#define XH_ERRNO_INVAL 1002
|
||||
#define XH_ERRNO_NOMEM 1003
|
||||
#define XH_ERRNO_REPEAT 1004
|
||||
#define XH_ERRNO_NOTFND 1005
|
||||
#define XH_ERRNO_BADMAPS 1006
|
||||
#define XH_ERRNO_FORMAT 1007
|
||||
#define XH_ERRNO_ELFINIT 1008
|
||||
#define XH_ERRNO_SEGVERR 1009
|
||||
|
||||
#endif
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// Created by caikelun on 2018-04-11.
|
||||
|
||||
#ifndef XH_LOG_H
|
||||
#define XH_LOG_H 1
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern android_LogPriority xh_log_priority;
|
||||
|
||||
#define XH_LOG_TAG "xhook"
|
||||
#define XH_LOG_DEBUG(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_DEBUG) __android_log_print(ANDROID_LOG_DEBUG, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
|
||||
#define XH_LOG_INFO(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_INFO) __android_log_print(ANDROID_LOG_INFO, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
|
||||
#define XH_LOG_WARN(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_WARN) __android_log_print(ANDROID_LOG_WARN, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
|
||||
#define XH_LOG_ERROR(fmt, ...) do{if(xh_log_priority <= ANDROID_LOG_ERROR) __android_log_print(ANDROID_LOG_ERROR, XH_LOG_TAG, fmt, ##__VA_ARGS__);}while(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,51 +0,0 @@
|
|||
// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// Created by caikelun on 2018-04-11.
|
||||
|
||||
#ifndef XH_UTILS_H
|
||||
#define XH_UTILS_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__LP64__)
|
||||
#define XH_UTIL_FMT_LEN "16"
|
||||
#define XH_UTIL_FMT_X "llx"
|
||||
#else
|
||||
#define XH_UTIL_FMT_LEN "8"
|
||||
#define XH_UTIL_FMT_X "x"
|
||||
#endif
|
||||
|
||||
#define XH_UTIL_FMT_FIXED_X XH_UTIL_FMT_LEN XH_UTIL_FMT_X
|
||||
#define XH_UTIL_FMT_FIXED_S XH_UTIL_FMT_LEN "s"
|
||||
|
||||
int xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot);
|
||||
int xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot);
|
||||
int xh_util_set_addr_protect(uintptr_t addr, unsigned int prot);
|
||||
void xh_util_flush_instruction_cache(uintptr_t addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,41 +0,0 @@
|
|||
// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// Created by caikelun on 2018-04-11.
|
||||
|
||||
#ifndef XH_VERSION_H
|
||||
#define XH_VERSION_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
unsigned int xh_version();
|
||||
|
||||
const char *xh_version_str();
|
||||
|
||||
const char *xh_version_str_full();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// Created by caikelun on 2018-04-11.
|
||||
|
||||
#ifndef XHOOK_H
|
||||
#define XHOOK_H 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define XHOOK_EXPORT __attribute__((visibility("default")))
|
||||
|
||||
int xhook_register(const char *pathname_regex_str, const char *symbol,
|
||||
void *new_func, void **old_func) XHOOK_EXPORT;
|
||||
|
||||
int xhook_ignore(const char *pathname_regex_str, const char *symbol) XHOOK_EXPORT;
|
||||
|
||||
int xhook_refresh(int async) XHOOK_EXPORT;
|
||||
|
||||
void xhook_clear() XHOOK_EXPORT;
|
||||
|
||||
void xhook_enable_debug(int flag) XHOOK_EXPORT;
|
||||
|
||||
void xhook_enable_sigsegv_protection(int flag) XHOOK_EXPORT;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,656 +0,0 @@
|
|||
// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// Created by caikelun on 2018-04-11.
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <regex.h>
|
||||
#include <setjmp.h>
|
||||
#include <errno.h>
|
||||
#include <queue.h>
|
||||
#include <tree.h>
|
||||
#include <xh_errno.h>
|
||||
#include <xh_log.h>
|
||||
#include <xh_elf.h>
|
||||
#include <xh_version.h>
|
||||
#include <xh_core.h>
|
||||
|
||||
#define XH_CORE_DEBUG 0
|
||||
|
||||
//registered hook info collection
|
||||
typedef struct xh_core_hook_info
|
||||
{
|
||||
#if XH_CORE_DEBUG
|
||||
char *pathname_regex_str;
|
||||
#endif
|
||||
regex_t pathname_regex;
|
||||
char *symbol;
|
||||
void *new_func;
|
||||
void **old_func;
|
||||
TAILQ_ENTRY(xh_core_hook_info,) link;
|
||||
} xh_core_hook_info_t;
|
||||
typedef TAILQ_HEAD(xh_core_hook_info_queue, xh_core_hook_info,) xh_core_hook_info_queue_t;
|
||||
|
||||
//ignored hook info collection
|
||||
typedef struct xh_core_ignore_info
|
||||
{
|
||||
#if XH_CORE_DEBUG
|
||||
char *pathname_regex_str;
|
||||
#endif
|
||||
regex_t pathname_regex;
|
||||
char *symbol; //NULL meaning for all symbols
|
||||
TAILQ_ENTRY(xh_core_ignore_info,) link;
|
||||
} xh_core_ignore_info_t;
|
||||
typedef TAILQ_HEAD(xh_core_ignore_info_queue, xh_core_ignore_info,) xh_core_ignore_info_queue_t;
|
||||
|
||||
//required info from /proc/self/maps
|
||||
typedef struct xh_core_map_info
|
||||
{
|
||||
char *pathname;
|
||||
uintptr_t base_addr;
|
||||
xh_elf_t elf;
|
||||
RB_ENTRY(xh_core_map_info) link;
|
||||
} xh_core_map_info_t;
|
||||
static __inline__ int xh_core_map_info_cmp(xh_core_map_info_t *a, xh_core_map_info_t *b)
|
||||
{
|
||||
return strcmp(a->pathname, b->pathname);
|
||||
}
|
||||
typedef RB_HEAD(xh_core_map_info_tree, xh_core_map_info) xh_core_map_info_tree_t;
|
||||
RB_GENERATE_STATIC(xh_core_map_info_tree, xh_core_map_info, link, xh_core_map_info_cmp)
|
||||
|
||||
//signal handler for SIGSEGV
|
||||
//for xh_elf_init(), xh_elf_hook(), xh_elf_check_elfheader()
|
||||
static int xh_core_sigsegv_enable = 1; //enable by default
|
||||
static struct sigaction xh_core_sigsegv_act_old;
|
||||
static volatile int xh_core_sigsegv_flag = 0;
|
||||
static sigjmp_buf xh_core_sigsegv_env;
|
||||
static void xh_core_sigsegv_handler(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
|
||||
if(xh_core_sigsegv_flag)
|
||||
siglongjmp(xh_core_sigsegv_env, 1);
|
||||
else
|
||||
sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL);
|
||||
}
|
||||
static int xh_core_add_sigsegv_handler()
|
||||
{
|
||||
struct sigaction act;
|
||||
|
||||
if(!xh_core_sigsegv_enable) return 0;
|
||||
|
||||
if(0 != sigemptyset(&act.sa_mask)) return (0 == errno ? XH_ERRNO_UNKNOWN : errno);
|
||||
act.sa_handler = xh_core_sigsegv_handler;
|
||||
|
||||
if(0 != sigaction(SIGSEGV, &act, &xh_core_sigsegv_act_old))
|
||||
return (0 == errno ? XH_ERRNO_UNKNOWN : errno);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static void xh_core_del_sigsegv_handler()
|
||||
{
|
||||
if(!xh_core_sigsegv_enable) return;
|
||||
|
||||
sigaction(SIGSEGV, &xh_core_sigsegv_act_old, NULL);
|
||||
}
|
||||
|
||||
|
||||
static xh_core_hook_info_queue_t xh_core_hook_info = TAILQ_HEAD_INITIALIZER(xh_core_hook_info);
|
||||
static xh_core_ignore_info_queue_t xh_core_ignore_info = TAILQ_HEAD_INITIALIZER(xh_core_ignore_info);
|
||||
static xh_core_map_info_tree_t xh_core_map_info = RB_INITIALIZER(&xh_core_map_info);
|
||||
static pthread_mutex_t xh_core_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t xh_core_cond = PTHREAD_COND_INITIALIZER;
|
||||
static volatile int xh_core_inited = 0;
|
||||
static volatile int xh_core_init_ok = 0;
|
||||
static volatile int xh_core_async_inited = 0;
|
||||
static volatile int xh_core_async_init_ok = 0;
|
||||
static pthread_mutex_t xh_core_refresh_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_t xh_core_refresh_thread_tid;
|
||||
static volatile int xh_core_refresh_thread_running = 0;
|
||||
static volatile int xh_core_refresh_thread_do = 0;
|
||||
|
||||
|
||||
int xh_core_register(const char *pathname_regex_str, const char *symbol,
|
||||
void *new_func, void **old_func)
|
||||
{
|
||||
xh_core_hook_info_t *hi;
|
||||
regex_t regex;
|
||||
|
||||
if(NULL == pathname_regex_str || NULL == symbol || NULL == new_func) return XH_ERRNO_INVAL;
|
||||
|
||||
if(xh_core_inited)
|
||||
{
|
||||
XH_LOG_ERROR("do not register hook after refresh(): %s, %s", pathname_regex_str, symbol);
|
||||
return XH_ERRNO_INVAL;
|
||||
}
|
||||
|
||||
if(0 != regcomp(®ex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL;
|
||||
|
||||
if(NULL == (hi = malloc(sizeof(xh_core_hook_info_t)))) return XH_ERRNO_NOMEM;
|
||||
if(NULL == (hi->symbol = strdup(symbol)))
|
||||
{
|
||||
free(hi);
|
||||
return XH_ERRNO_NOMEM;
|
||||
}
|
||||
#if XH_CORE_DEBUG
|
||||
if(NULL == (hi->pathname_regex_str = strdup(pathname_regex_str)))
|
||||
{
|
||||
free(hi->symbol);
|
||||
free(hi);
|
||||
return XH_ERRNO_NOMEM;
|
||||
}
|
||||
#endif
|
||||
hi->pathname_regex = regex;
|
||||
hi->new_func = new_func;
|
||||
hi->old_func = old_func;
|
||||
|
||||
pthread_mutex_lock(&xh_core_mutex);
|
||||
TAILQ_INSERT_TAIL(&xh_core_hook_info, hi, link);
|
||||
pthread_mutex_unlock(&xh_core_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xh_core_ignore(const char *pathname_regex_str, const char *symbol)
|
||||
{
|
||||
xh_core_ignore_info_t *ii;
|
||||
regex_t regex;
|
||||
|
||||
if(NULL == pathname_regex_str) return XH_ERRNO_INVAL;
|
||||
|
||||
if(xh_core_inited)
|
||||
{
|
||||
XH_LOG_ERROR("do not ignore hook after refresh(): %s, %s", pathname_regex_str, symbol ? symbol : "ALL");
|
||||
return XH_ERRNO_INVAL;
|
||||
}
|
||||
|
||||
if(0 != regcomp(®ex, pathname_regex_str, REG_NOSUB)) return XH_ERRNO_INVAL;
|
||||
|
||||
if(NULL == (ii = malloc(sizeof(xh_core_ignore_info_t)))) return XH_ERRNO_NOMEM;
|
||||
if(NULL != symbol)
|
||||
{
|
||||
if(NULL == (ii->symbol = strdup(symbol)))
|
||||
{
|
||||
free(ii);
|
||||
return XH_ERRNO_NOMEM;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ii->symbol = NULL; //ignore all symbols
|
||||
}
|
||||
#if XH_CORE_DEBUG
|
||||
if(NULL == (ii->pathname_regex_str = strdup(pathname_regex_str)))
|
||||
{
|
||||
free(ii->symbol);
|
||||
free(ii);
|
||||
return XH_ERRNO_NOMEM;
|
||||
}
|
||||
#endif
|
||||
ii->pathname_regex = regex;
|
||||
|
||||
pthread_mutex_lock(&xh_core_mutex);
|
||||
TAILQ_INSERT_TAIL(&xh_core_ignore_info, ii, link);
|
||||
pthread_mutex_unlock(&xh_core_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xh_core_check_elf_header(uintptr_t base_addr, const char *pathname)
|
||||
{
|
||||
if(!xh_core_sigsegv_enable)
|
||||
{
|
||||
return xh_elf_check_elfheader(base_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret = XH_ERRNO_UNKNOWN;
|
||||
|
||||
xh_core_sigsegv_flag = 1;
|
||||
if(0 == sigsetjmp(xh_core_sigsegv_env, 1))
|
||||
{
|
||||
ret = xh_elf_check_elfheader(base_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = XH_ERRNO_SEGVERR;
|
||||
XH_LOG_WARN("catch SIGSEGV when check_elfheader: %s", pathname);
|
||||
}
|
||||
xh_core_sigsegv_flag = 0;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static void xh_core_hook_impl(xh_core_map_info_t *mi)
|
||||
{
|
||||
//init
|
||||
if(0 != xh_elf_init(&(mi->elf), mi->base_addr, mi->pathname)) return;
|
||||
|
||||
//hook
|
||||
xh_core_hook_info_t *hi;
|
||||
xh_core_ignore_info_t *ii;
|
||||
int ignore;
|
||||
TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info
|
||||
{
|
||||
if(0 == regexec(&(hi->pathname_regex), mi->pathname, 0, NULL, 0))
|
||||
{
|
||||
ignore = 0;
|
||||
TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info
|
||||
{
|
||||
if(0 == regexec(&(ii->pathname_regex), mi->pathname, 0, NULL, 0))
|
||||
{
|
||||
if(NULL == ii->symbol) //ignore all symbols
|
||||
return;
|
||||
|
||||
if(0 == strcmp(ii->symbol, hi->symbol)) //ignore the current symbol
|
||||
{
|
||||
ignore = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(0 == ignore)
|
||||
xh_elf_hook(&(mi->elf), hi->symbol, hi->new_func, hi->old_func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void xh_core_hook(xh_core_map_info_t *mi)
|
||||
{
|
||||
if(!xh_core_sigsegv_enable)
|
||||
{
|
||||
xh_core_hook_impl(mi);
|
||||
}
|
||||
else
|
||||
{
|
||||
xh_core_sigsegv_flag = 1;
|
||||
if(0 == sigsetjmp(xh_core_sigsegv_env, 1))
|
||||
{
|
||||
xh_core_hook_impl(mi);
|
||||
}
|
||||
else
|
||||
{
|
||||
XH_LOG_WARN("catch SIGSEGV when init or hook: %s", mi->pathname);
|
||||
}
|
||||
xh_core_sigsegv_flag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void xh_core_refresh_impl()
|
||||
{
|
||||
char line[512];
|
||||
FILE *fp;
|
||||
uintptr_t base_addr;
|
||||
char perm[5];
|
||||
unsigned long offset;
|
||||
int pathname_pos;
|
||||
char *pathname;
|
||||
size_t pathname_len;
|
||||
xh_core_map_info_t *mi, *mi_tmp;
|
||||
xh_core_map_info_t mi_key;
|
||||
xh_core_hook_info_t *hi;
|
||||
xh_core_ignore_info_t *ii;
|
||||
int match;
|
||||
xh_core_map_info_tree_t map_info_refreshed = RB_INITIALIZER(&map_info_refreshed);
|
||||
|
||||
if(NULL == (fp = fopen("/proc/self/maps", "r")))
|
||||
{
|
||||
XH_LOG_ERROR("fopen /proc/self/maps failed");
|
||||
return;
|
||||
}
|
||||
|
||||
while(fgets(line, sizeof(line), fp))
|
||||
{
|
||||
if(sscanf(line, "%"PRIxPTR"-%*lx %4s %lx %*x:%*x %*d%n", &base_addr, perm, &offset, &pathname_pos) != 3) continue;
|
||||
|
||||
//check permission
|
||||
if(perm[0] != 'r') continue;
|
||||
if(perm[3] != 'p') continue; //do not touch the shared memory
|
||||
|
||||
//check offset
|
||||
//
|
||||
//We are trying to find ELF header in memory.
|
||||
//It can only be found at the beginning of a mapped memory regions
|
||||
//whose offset is 0.
|
||||
if(0 != offset) continue;
|
||||
|
||||
//get pathname
|
||||
while(isspace(line[pathname_pos]) && pathname_pos < (int)(sizeof(line) - 1))
|
||||
pathname_pos += 1;
|
||||
if(pathname_pos >= (int)(sizeof(line) - 1)) continue;
|
||||
pathname = line + pathname_pos;
|
||||
pathname_len = strlen(pathname);
|
||||
if(0 == pathname_len) continue;
|
||||
if(pathname[pathname_len - 1] == '\n')
|
||||
{
|
||||
pathname[pathname_len - 1] = '\0';
|
||||
pathname_len -= 1;
|
||||
}
|
||||
if(0 == pathname_len) continue;
|
||||
if('[' == pathname[0]) continue;
|
||||
|
||||
//check pathname
|
||||
//if we need to hook this elf?
|
||||
match = 0;
|
||||
TAILQ_FOREACH(hi, &xh_core_hook_info, link) //find hook info
|
||||
{
|
||||
if(0 == regexec(&(hi->pathname_regex), pathname, 0, NULL, 0))
|
||||
{
|
||||
TAILQ_FOREACH(ii, &xh_core_ignore_info, link) //find ignore info
|
||||
{
|
||||
if(0 == regexec(&(ii->pathname_regex), pathname, 0, NULL, 0))
|
||||
{
|
||||
if(NULL == ii->symbol)
|
||||
goto check_finished;
|
||||
|
||||
if(0 == strcmp(ii->symbol, hi->symbol))
|
||||
goto check_continue;
|
||||
}
|
||||
}
|
||||
|
||||
match = 1;
|
||||
check_continue:
|
||||
break;
|
||||
}
|
||||
}
|
||||
check_finished:
|
||||
if(0 == match) continue;
|
||||
|
||||
//check elf header format
|
||||
//We are trying to do ELF header checking as late as possible.
|
||||
if(0 != xh_core_check_elf_header(base_addr, pathname)) continue;
|
||||
|
||||
//check existed map item
|
||||
mi_key.pathname = pathname;
|
||||
if(NULL != (mi = RB_FIND(xh_core_map_info_tree, &xh_core_map_info, &mi_key)))
|
||||
{
|
||||
//exist
|
||||
RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);
|
||||
|
||||
//repeated?
|
||||
//We only keep the first one, that is the real base address
|
||||
if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi))
|
||||
{
|
||||
#if XH_CORE_DEBUG
|
||||
XH_LOG_DEBUG("repeated map info when update: %s", line);
|
||||
#endif
|
||||
free(mi->pathname);
|
||||
free(mi);
|
||||
continue;
|
||||
}
|
||||
|
||||
//re-hook if base_addr changed
|
||||
if(mi->base_addr != base_addr)
|
||||
{
|
||||
mi->base_addr = base_addr;
|
||||
xh_core_hook(mi);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//not exist, create a new map info
|
||||
if(NULL == (mi = (xh_core_map_info_t *)malloc(sizeof(xh_core_map_info_t)))) continue;
|
||||
if(NULL == (mi->pathname = strdup(pathname)))
|
||||
{
|
||||
free(mi);
|
||||
continue;
|
||||
}
|
||||
mi->base_addr = base_addr;
|
||||
|
||||
//repeated?
|
||||
//We only keep the first one, that is the real base address
|
||||
if(NULL != RB_INSERT(xh_core_map_info_tree, &map_info_refreshed, mi))
|
||||
{
|
||||
#if XH_CORE_DEBUG
|
||||
XH_LOG_DEBUG("repeated map info when create: %s", line);
|
||||
#endif
|
||||
free(mi->pathname);
|
||||
free(mi);
|
||||
continue;
|
||||
}
|
||||
|
||||
//hook
|
||||
xh_core_hook(mi); //hook
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
//free all missing map item, maybe dlclosed?
|
||||
RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp)
|
||||
{
|
||||
#if XH_CORE_DEBUG
|
||||
XH_LOG_DEBUG("remove missing map info: %s", mi->pathname);
|
||||
#endif
|
||||
RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);
|
||||
if(mi->pathname) free(mi->pathname);
|
||||
free(mi);
|
||||
}
|
||||
|
||||
//save the new refreshed map info tree
|
||||
xh_core_map_info = map_info_refreshed;
|
||||
|
||||
XH_LOG_INFO("map refreshed");
|
||||
|
||||
#if XH_CORE_DEBUG
|
||||
RB_FOREACH(mi, xh_core_map_info_tree, &xh_core_map_info)
|
||||
XH_LOG_DEBUG(" %"PRIxPTR" %s\n", mi->base_addr, mi->pathname);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void *xh_core_refresh_thread_func(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
pthread_setname_np(pthread_self(), "xh_refresh_loop");
|
||||
|
||||
while(xh_core_refresh_thread_running)
|
||||
{
|
||||
//waiting for a refresh task or exit
|
||||
pthread_mutex_lock(&xh_core_mutex);
|
||||
while(!xh_core_refresh_thread_do && xh_core_refresh_thread_running)
|
||||
{
|
||||
pthread_cond_wait(&xh_core_cond, &xh_core_mutex);
|
||||
}
|
||||
if(!xh_core_refresh_thread_running)
|
||||
{
|
||||
pthread_mutex_unlock(&xh_core_mutex);
|
||||
break;
|
||||
}
|
||||
xh_core_refresh_thread_do = 0;
|
||||
pthread_mutex_unlock(&xh_core_mutex);
|
||||
|
||||
//refresh
|
||||
pthread_mutex_lock(&xh_core_refresh_mutex);
|
||||
xh_core_refresh_impl();
|
||||
pthread_mutex_unlock(&xh_core_refresh_mutex);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void xh_core_init_once()
|
||||
{
|
||||
if(xh_core_inited) return;
|
||||
|
||||
pthread_mutex_lock(&xh_core_mutex);
|
||||
|
||||
if(xh_core_inited) goto end;
|
||||
|
||||
xh_core_inited = 1;
|
||||
|
||||
//dump debug info
|
||||
XH_LOG_INFO("%s\n", xh_version_str_full());
|
||||
#if XH_CORE_DEBUG
|
||||
xh_core_hook_info_t *hi;
|
||||
TAILQ_FOREACH(hi, &xh_core_hook_info, link)
|
||||
XH_LOG_INFO(" hook: %s @ %s, (%p, %p)\n", hi->symbol, hi->pathname_regex_str,
|
||||
hi->new_func, hi->old_func);
|
||||
xh_core_ignore_info_t *ii;
|
||||
TAILQ_FOREACH(ii, &xh_core_ignore_info, link)
|
||||
XH_LOG_INFO(" ignore: %s @ %s\n", ii->symbol ? ii->symbol : "ALL ",
|
||||
ii->pathname_regex_str);
|
||||
#endif
|
||||
|
||||
//register signal handler
|
||||
if(0 != xh_core_add_sigsegv_handler()) goto end;
|
||||
|
||||
//OK
|
||||
xh_core_init_ok = 1;
|
||||
|
||||
end:
|
||||
pthread_mutex_unlock(&xh_core_mutex);
|
||||
}
|
||||
|
||||
static void xh_core_init_async_once()
|
||||
{
|
||||
if(xh_core_async_inited) return;
|
||||
|
||||
pthread_mutex_lock(&xh_core_mutex);
|
||||
|
||||
if(xh_core_async_inited) goto end;
|
||||
|
||||
xh_core_async_inited = 1;
|
||||
|
||||
//create async refresh thread
|
||||
xh_core_refresh_thread_running = 1;
|
||||
if(0 != pthread_create(&xh_core_refresh_thread_tid, NULL, &xh_core_refresh_thread_func, NULL))
|
||||
{
|
||||
xh_core_refresh_thread_running = 0;
|
||||
goto end;
|
||||
}
|
||||
|
||||
//OK
|
||||
xh_core_async_init_ok = 1;
|
||||
|
||||
end:
|
||||
pthread_mutex_unlock(&xh_core_mutex);
|
||||
}
|
||||
|
||||
int xh_core_refresh(int async)
|
||||
{
|
||||
//init
|
||||
xh_core_init_once();
|
||||
if(!xh_core_init_ok) return XH_ERRNO_UNKNOWN;
|
||||
|
||||
if(async)
|
||||
{
|
||||
//init for async
|
||||
xh_core_init_async_once();
|
||||
if(!xh_core_async_init_ok) return XH_ERRNO_UNKNOWN;
|
||||
|
||||
//refresh async
|
||||
pthread_mutex_lock(&xh_core_mutex);
|
||||
xh_core_refresh_thread_do = 1;
|
||||
pthread_cond_signal(&xh_core_cond);
|
||||
pthread_mutex_unlock(&xh_core_mutex);
|
||||
}
|
||||
else
|
||||
{
|
||||
//refresh sync
|
||||
pthread_mutex_lock(&xh_core_refresh_mutex);
|
||||
xh_core_refresh_impl();
|
||||
pthread_mutex_unlock(&xh_core_refresh_mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xh_core_clear()
|
||||
{
|
||||
//stop the async refresh thread
|
||||
if(xh_core_async_init_ok)
|
||||
{
|
||||
pthread_mutex_lock(&xh_core_mutex);
|
||||
xh_core_refresh_thread_running = 0;
|
||||
pthread_cond_signal(&xh_core_cond);
|
||||
pthread_mutex_unlock(&xh_core_mutex);
|
||||
|
||||
pthread_join(xh_core_refresh_thread_tid, NULL);
|
||||
xh_core_async_init_ok = 0;
|
||||
}
|
||||
xh_core_async_inited = 0;
|
||||
|
||||
//unregister the sig handler
|
||||
if(xh_core_init_ok)
|
||||
{
|
||||
xh_core_del_sigsegv_handler();
|
||||
xh_core_init_ok = 0;
|
||||
}
|
||||
xh_core_inited = 0;
|
||||
|
||||
pthread_mutex_lock(&xh_core_mutex);
|
||||
pthread_mutex_lock(&xh_core_refresh_mutex);
|
||||
|
||||
//free all map info
|
||||
xh_core_map_info_t *mi, *mi_tmp;
|
||||
RB_FOREACH_SAFE(mi, xh_core_map_info_tree, &xh_core_map_info, mi_tmp)
|
||||
{
|
||||
RB_REMOVE(xh_core_map_info_tree, &xh_core_map_info, mi);
|
||||
if(mi->pathname) free(mi->pathname);
|
||||
free(mi);
|
||||
}
|
||||
|
||||
//free all hook info
|
||||
xh_core_hook_info_t *hi, *hi_tmp;
|
||||
TAILQ_FOREACH_SAFE(hi, &xh_core_hook_info, link, hi_tmp)
|
||||
{
|
||||
TAILQ_REMOVE(&xh_core_hook_info, hi, link);
|
||||
#if XH_CORE_DEBUG
|
||||
free(hi->pathname_regex_str);
|
||||
#endif
|
||||
regfree(&(hi->pathname_regex));
|
||||
free(hi->symbol);
|
||||
free(hi);
|
||||
}
|
||||
|
||||
//free all ignore info
|
||||
xh_core_ignore_info_t *ii, *ii_tmp;
|
||||
TAILQ_FOREACH_SAFE(ii, &xh_core_ignore_info, link, ii_tmp)
|
||||
{
|
||||
TAILQ_REMOVE(&xh_core_ignore_info, ii, link);
|
||||
#if XH_CORE_DEBUG
|
||||
free(ii->pathname_regex_str);
|
||||
#endif
|
||||
regfree(&(ii->pathname_regex));
|
||||
free(ii->symbol);
|
||||
free(ii);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&xh_core_refresh_mutex);
|
||||
pthread_mutex_unlock(&xh_core_mutex);
|
||||
}
|
||||
|
||||
void xh_core_enable_debug(int flag)
|
||||
{
|
||||
xh_log_priority = (flag ? ANDROID_LOG_DEBUG : ANDROID_LOG_WARN);
|
||||
}
|
||||
|
||||
void xh_core_enable_sigsegv_protection(int flag)
|
||||
{
|
||||
xh_core_sigsegv_enable = (flag ? 1 : 0);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,59 +0,0 @@
|
|||
// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// Created by caikelun on 2018-04-11.
|
||||
|
||||
#include <jni.h>
|
||||
#include <xhook.h>
|
||||
|
||||
#define JNI_API_DEF(f) Java_com_qiyi_xhook_NativeHandler_##f
|
||||
|
||||
JNIEXPORT jint JNI_API_DEF(refresh)(JNIEnv *env, jobject obj, jboolean async)
|
||||
{
|
||||
(void)env;
|
||||
(void)obj;
|
||||
|
||||
return xhook_refresh(async ? 1 : 0);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNI_API_DEF(clear)(JNIEnv *env, jobject obj)
|
||||
{
|
||||
(void)env;
|
||||
(void)obj;
|
||||
|
||||
xhook_clear();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNI_API_DEF(enableDebug)(JNIEnv *env, jobject obj, jboolean flag)
|
||||
{
|
||||
(void)env;
|
||||
(void)obj;
|
||||
|
||||
xhook_enable_debug(flag ? 1 : 0);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNI_API_DEF(enableSigSegvProtection)(JNIEnv *env, jobject obj, jboolean flag)
|
||||
{
|
||||
(void)env;
|
||||
(void)obj;
|
||||
|
||||
xhook_enable_sigsegv_protection(flag ? 1 : 0);
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// Created by caikelun on 2018-04-11.
|
||||
|
||||
#include <android/log.h>
|
||||
#include <xh_log.h>
|
||||
|
||||
android_LogPriority xh_log_priority = ANDROID_LOG_WARN;
|
|
@ -1,121 +0,0 @@
|
|||
// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// Created by caikelun on 2018-04-11.
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <elf.h>
|
||||
#include <link.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <xh_util.h>
|
||||
#include <xh_errno.h>
|
||||
#include <xh_log.h>
|
||||
|
||||
#define PAGE_START(addr) ((addr) & PAGE_MASK)
|
||||
#define PAGE_END(addr) (PAGE_START(addr + sizeof(uintptr_t) - 1) + PAGE_SIZE)
|
||||
#define PAGE_COVER(addr) (PAGE_END(addr) - PAGE_START(addr))
|
||||
|
||||
int xh_util_get_mem_protect(uintptr_t addr, size_t len, const char *pathname, unsigned int *prot)
|
||||
{
|
||||
uintptr_t start_addr = addr;
|
||||
uintptr_t end_addr = addr + len;
|
||||
FILE *fp;
|
||||
char line[512];
|
||||
uintptr_t start, end;
|
||||
char perm[5];
|
||||
int load0 = 1;
|
||||
int found_all = 0;
|
||||
|
||||
*prot = 0;
|
||||
|
||||
if(NULL == (fp = fopen("/proc/self/maps", "r"))) return XH_ERRNO_BADMAPS;
|
||||
|
||||
while(fgets(line, sizeof(line), fp))
|
||||
{
|
||||
if(NULL != pathname)
|
||||
if(NULL == strstr(line, pathname)) continue;
|
||||
|
||||
if(sscanf(line, "%"PRIxPTR"-%"PRIxPTR" %4s ", &start, &end, perm) != 3) continue;
|
||||
|
||||
if(perm[3] != 'p') continue;
|
||||
|
||||
if(start_addr >= start && start_addr < end)
|
||||
{
|
||||
if(load0)
|
||||
{
|
||||
//first load segment
|
||||
if(perm[0] == 'r') *prot |= PROT_READ;
|
||||
if(perm[1] == 'w') *prot |= PROT_WRITE;
|
||||
if(perm[2] == 'x') *prot |= PROT_EXEC;
|
||||
load0 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//others
|
||||
if(perm[0] != 'r') *prot &= ~PROT_READ;
|
||||
if(perm[1] != 'w') *prot &= ~PROT_WRITE;
|
||||
if(perm[2] != 'x') *prot &= ~PROT_EXEC;
|
||||
}
|
||||
|
||||
if(end_addr <= end)
|
||||
{
|
||||
found_all = 1;
|
||||
break; //finished
|
||||
}
|
||||
else
|
||||
{
|
||||
start_addr = end; //try to find the next load segment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if(!found_all) return XH_ERRNO_SEGVERR;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xh_util_get_addr_protect(uintptr_t addr, const char *pathname, unsigned int *prot)
|
||||
{
|
||||
return xh_util_get_mem_protect(addr, sizeof(addr), pathname, prot);
|
||||
}
|
||||
|
||||
int xh_util_set_addr_protect(uintptr_t addr, unsigned int prot)
|
||||
{
|
||||
if(0 != mprotect((void *)PAGE_START(addr), PAGE_COVER(addr), (int)prot))
|
||||
return 0 == errno ? XH_ERRNO_UNKNOWN : errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xh_util_flush_instruction_cache(uintptr_t addr)
|
||||
{
|
||||
__builtin___clear_cache((void *)PAGE_START(addr), (void *)PAGE_END(addr));
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// Created by caikelun on 2018-04-11.
|
||||
|
||||
#include <xh_version.h>
|
||||
|
||||
#define XH_VERSION_MAJOR 1
|
||||
#define XH_VERSION_MINOR 2
|
||||
#define XH_VERSION_EXTRA 0
|
||||
|
||||
#define XH_VERSION ((XH_VERSION_MAJOR << 16) | (XH_VERSION_MINOR << 8) | (XH_VERSION_EXTRA))
|
||||
|
||||
#define XH_VERSION_TO_STR_HELPER(x) #x
|
||||
#define XH_VERSION_TO_STR(x) XH_VERSION_TO_STR_HELPER(x)
|
||||
|
||||
#define XH_VERSION_STR XH_VERSION_TO_STR(XH_VERSION_MAJOR) "." \
|
||||
XH_VERSION_TO_STR(XH_VERSION_MINOR) "." \
|
||||
XH_VERSION_TO_STR(XH_VERSION_EXTRA)
|
||||
|
||||
#if defined(__arm__)
|
||||
#define XH_VERSION_ARCH "arm"
|
||||
#elif defined(__aarch64__)
|
||||
#define XH_VERSION_ARCH "aarch64"
|
||||
#elif defined(__i386__)
|
||||
#define XH_VERSION_ARCH "x86"
|
||||
#elif defined(__x86_64__)
|
||||
#define XH_VERSION_ARCH "x86_64"
|
||||
#else
|
||||
#define XH_VERSION_ARCH "unknown"
|
||||
#endif
|
||||
|
||||
#define XH_VERSION_STR_FULL "libxhook "XH_VERSION_STR" ("XH_VERSION_ARCH")"
|
||||
|
||||
unsigned int xh_version()
|
||||
{
|
||||
return XH_VERSION;
|
||||
}
|
||||
|
||||
const char *xh_version_str()
|
||||
{
|
||||
return XH_VERSION_STR;
|
||||
}
|
||||
|
||||
const char *xh_version_str_full()
|
||||
{
|
||||
return XH_VERSION_STR_FULL;
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
// Copyright (c) 2018-present, iQIYI, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
// Created by caikelun on 2018-04-11.
|
||||
|
||||
#include <xh_core.h>
|
||||
#include <xhook.h>
|
||||
|
||||
int xhook_register(const char *pathname_regex_str, const char *symbol,
|
||||
void *new_func, void **old_func)
|
||||
{
|
||||
return xh_core_register(pathname_regex_str, symbol, new_func, old_func);
|
||||
}
|
||||
|
||||
int xhook_ignore(const char *pathname_regex_str, const char *symbol)
|
||||
{
|
||||
return xh_core_ignore(pathname_regex_str, symbol);
|
||||
}
|
||||
|
||||
int xhook_refresh(int async)
|
||||
{
|
||||
return xh_core_refresh(async);
|
||||
}
|
||||
|
||||
void xhook_clear()
|
||||
{
|
||||
return xh_core_clear();
|
||||
}
|
||||
|
||||
void xhook_enable_debug(int flag)
|
||||
{
|
||||
return xh_core_enable_debug(flag);
|
||||
}
|
||||
|
||||
void xhook_enable_sigsegv_protection(int flag)
|
||||
{
|
||||
return xh_core_enable_sigsegv_protection(flag);
|
||||
}
|
Loading…
Reference in New Issue