mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-25 11:05:34 +00:00
664 lines
19 KiB
C
664 lines
19 KiB
C
|
// 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;
|
||
|
unsigned long inode;
|
||
|
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)
|
||
|
{
|
||
|
int path_cmp = strcmp(a->pathname, b->pathname);
|
||
|
if (path_cmp == 0) {
|
||
|
return a->inode - b->inode;
|
||
|
} else {
|
||
|
return path_cmp;
|
||
|
}
|
||
|
}
|
||
|
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 %lu", mi->pathname, mi->inode);
|
||
|
}
|
||
|
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;
|
||
|
unsigned long inode;
|
||
|
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 %lu %n", &base_addr, perm, &offset, &inode, &pathname_pos) != 4) 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;
|
||
|
mi_key.inode = inode;
|
||
|
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->inode = inode;
|
||
|
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))
|
||
|
{
|
||
|
XH_LOG_WARN("repeated map info when create: %s", line);
|
||
|
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);
|
||
|
}
|