當前位置:首頁 » 安卓系統 » androiddlopen

androiddlopen

發布時間: 2023-03-25 19:53:11

㈠ Android硬體抽象層模塊編寫規范

硬體抽象層模塊編寫規范

硬體抽象層最終都會生成.so文件,放到系統對應的目錄中。在系統使用的時候,系統會去對應目錄下載入so文件,實現硬體抽象層的功能。因此硬體抽象層的載入過程就是我們使用so的一個介面。先了解載入過程從源頭了解抽象層模塊兒的編寫規范。

1、硬體抽象層載入過程

系統在載入so的過程中,會去兩個目錄下查找對應id的so文件。這兩個目錄分別是/system/lib/hw和/vendor/lib/hw。

so文件的名字分為兩個部分例如id.prop.so,第一部分是模塊id。第二部分是系統prop的值,獲取順序為「ro.hardware」、「ro.procat.board」、「ro.board.platform」、「ro.arch」,如果prop都找不到的話,就用default。(不是找不到prop的值,是找不到prop值對應的so文件)。

負責載入硬體抽象層模塊的函數是hw_get_mole,所在的文件是/hardware/libhardware/hardware.c如下:

/** Base path of the hal moles */

#if defined(__LP64__)

#define HAL_LIBRARY_PATH1 "/system/lib64/hw"

#define HAL_LIBRARY_PATH2 "/vendor/lib64/hw"

#else

#define HAL_LIBRARY_PATH1 "/system/lib/hw"

#define HAL_LIBRARY_PATH2 "/vendor/lib/hw"

#endif

/**

 * There are a set of variant filename for moles. The form of the filename

* is ".variant.so" so for the led mole the Dream variants 

 * of base "ro.proct.board", "ro.board.platform" and "ro.arch" would be:

 *

 * led.trout.so

 * led.msm7k.so

 * led.ARMV6.so

 * led.default.so

 */

static const char *variant_keys[] = {

    "ro.hardware",  /* This goes first so that it can pick up a different

                       file on the emulator. */

    "ro.proct.board",

    "ro.board.platform",

    "ro.arch"

};

static const int HAL_VARIANT_KEYS_COUNT =

    (sizeof(variant_keys)/sizeof(variant_keys[0]));

/**

 * Load the file defined by the variant and if successful

 * return the dlopen handle and the hmi.

 * @return 0 = success, !0 = failure.

 */

static int load(const char *id,

        const char *path,

        const struct hw_mole_t **pHmi)

{

    int status;

    void *handle;

    struct hw_mole_t *hmi;

    /*

     * load the symbols resolving undefined symbols before

     * dlopen returns. Since RTLD_GLOBAL is not or'd in with

     * RTLD_NOW the external symbols will not be global

     */

    handle = dlopen(path, RTLD_NOW);

    if (handle == NULL) {

        char const *err_str = dlerror();

        ALOGE("load: mole=%s\n%s", path, err_str?err_str:"unknown");

        status = -EINVAL;

        goto done;

    }

    /* Get the address of the struct hal_mole_info. */

    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;

    hmi = (struct hw_mole_t *)dlsym(handle, sym);

    if (hmi == NULL) {

        ALOGE("load: couldn't find symbol %s", sym);

        status = -EINVAL;

        goto done;

    }

    /* Check that the id matches */

    if (strcmp(id, hmi->id) != 0) {

        ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);

        status = -EINVAL;

        goto done;

    }

    hmi->dso = handle;

    /* success */

    status = 0;

    done:

    if (status != 0) {

        hmi = NULL;

        if (handle != NULL) {

            dlclose(handle);

            handle = NULL;

        }

    } else {

        ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",

                id, path, *pHmi, handle);

    }

    *pHmi = hmi;

    return status;

}

/*

 * Check if a HAL with given name and subname exists, if so return 0, otherwise

 * otherwise return negative.  On success path will contain the path to the HAL.

 */

static int hw_mole_exists(char *path, size_t path_len, const char *name,

                            const char *subname)

{

    snprintf(path, path_len, "%s/%s.%s.so",

             HAL_LIBRARY_PATH2, name, subname);

    if (access(path, R_OK) == 0)

        return 0;

    snprintf(path, path_len, "%s/%s.%s.so",

             HAL_LIBRARY_PATH1, name, subname);

    if (access(path, R_OK) == 0)

        return 0;

    return -ENOENT;

}

int hw_get_mole_by_class(const char *class_id, const char *inst,

                           const struct hw_mole_t **mole)

{

    int i;

    char prop[PATH_MAX];

    char path[PATH_MAX];

    char name[PATH_MAX];

    char prop_name[PATH_MAX];

    if (inst)

        snprintf(name, PATH_MAX, "%s.%s", class_id, inst);

    else

        strlcpy(name, class_id, PATH_MAX);

    /*

     * Here we rely on the fact that calling dlopen multiple times on

     * the same .so will simply increment a refcount (and not load

     * a new of the library).

     * We also assume that dlopen() is thread-safe.

     */

    /* First try a property specific to the class and possibly instance */

    snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);

    if (property_get(prop_name, prop, NULL) > 0) {

        if (hw_mole_exists(path, sizeof(path), name, prop) == 0) {

            goto found;

        }

    }

    /* Loop through the configuration variants looking for a mole */

    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {

        if (property_get(variant_keys[i], prop, NULL) == 0) {

            continue;

        }

        if (hw_mole_exists(path, sizeof(path), name, prop) == 0) {

            goto found;

        }

    }

    /* Nothing found, try the default */

    if (hw_mole_exists(path, sizeof(path), name, "default") == 0) {

        goto found;

    }

    return -ENOENT;

found:

    /* load the mole, if this fails, we're doomed, and we should not try

     * to load a different variant. */

    return load(class_id, path, mole);

}

int hw_get_mole(const char *id, const struct hw_mole_t **mole)

{

    return hw_get_mole_by_class(id, NULL, mole);

}

找到so文件之後,調用方法load方法去載入對應的so文件,並返回hw_mole_t結構體。load方法源碼在上面程序中。

load方法首先調用dlopen載入對應的so文件到內存中。然後用dlsym方法找到變數HAL_MODULE_INFO_SYM_AS_STR符號對應的地址,這個地址也就是一個hw_mole_t結構體,然後從這個結構體中拿出id比對load方法出入的id是否一致,如果是的話表示打開成功。載入過程完成。

HAL_MODULE_INFO_SYM_AS_STR這個符號值為HMI,也就是必須要保證這個符號之後是一個hw_mole_t。接下來的規范中有這個要求。

到此,模塊載入完成

2、硬體抽象層模塊編寫規范

硬體抽象層有兩個結構體,一個是hw_mole_t和hw_device_t,定義在hardware.h中。

首先說一下hw_mole_t的編寫規范。

1、必須要有一個「自定義硬體抽象層結構體」,且結構體第一個變數類型要為hw_mole_t。

2、必須存在一個HARDWARE_MODULE_INFO_TAG的符號,且指向「自定義硬體抽象層結構體」。在載入的時候根據這個符號找到地址,並把地址的轉變為hw_mole_t,這也是為什麼第一條中hw_mole_t必須要在第一個的原因。

3、hw_mole_t的tag必須為HARDWARE_MODULE_TAG

4、結構體中要有一個方法列表,其中要有一個open方法。用open方法獲得hw_device_t

接下來說一下hw_device_t的編寫規范

1、必須要有一個「自定義硬體設備結構體」,且結構體第一個變數類型要為hw_device_t。

2、hw_device_t的tag必須為HARDWARE_DEVICE_TAG

3、要有一個close函數指針,來關閉設備

按照上面規范編寫的硬體抽象層就可以由系統載入並正確獲取到device。具體的應用層邏輯在device中實現。

㈡ 如何調試Android SO中的init函數

1.Root設備

Root許可權下才能快樂調試。
使用市面上的各種Root師傅工具。

2.連接設備

將設備打開調試模式在開發者選項里。
將IDA安裝目錄中dbgsrv文件夾下的android_server推送到設備系統目錄並賦可執行許可權。在高於IDA6.6版本才能調試高版本android,此時調試低版本Android SO時,需要使用的是android_nonpipe。
在PC端輸入命令:

adb shell su
adb shell android_server的路徑/android_server

保持上面窗口,在命令行窗口進行埠轉發:

adb forward tcp:23946 tcp:23946

為什麼是23946呢,IDA和push進設備的android_server默認用都用23946埠進行通訊。當然可以修改。

3.打開IDA

附加或者啟動進程的過程不再多言。

4.定位INIT函數

比較便捷的方法是找一份與設備同系統版本號的android源碼。解析執行SO文件的地方在linker.c(cpp)中。
因為不同版本有差異,我就不上圖了。

高版本時在do_dlopen()下的CallConstructors()裡面,但是編譯系統時往往將其和find_library融合在其父函數中,查找時需注意。一個簡便方法是源碼中搜索「INIT」四個字.
先將設備中的linker pull出來用IDA分析來確定調用INIT的具體位置。
因為linker在Android進程中載入非常早,所以它在IDA中的地址可以不用修正直接拿來用。

5.下斷在INIT

下斷點後,執行Apk中觸發載入該SO的功能。
正常情況下就能停在該SO的INIT前了。

㈢ 手機調試Android程序出異常時不列印堆棧信息

列印堆棧是調試的常用方法,一般在系統異常時,我們可以將異常情況下的堆棧列印出來,這樣十分方便錯誤查找。實際上還有另外一個非常有用的功能:分析代碼的行為。android代碼太過龐大復雜了,完全的靜態分析經常是無從下手,因此通過列印堆棧的動態分析也十分必要。

Android列印堆棧的方法,簡單歸類一下
1. zygote的堆棧mp
實際上這個可以同時mp java線程及native線程的堆棧,對於java線程,java堆棧和native堆棧都可以得到。
使用方法很簡單,直接在adb shell或串口中輸入:
[plain] view plain
kill -3 <pid>
輸出的trace會保存在 /data/anr/traces.txt文件中。這個需要注意,如果沒有 /data/anr/這個目錄或/data/anr/traces.txt這個文件,需要手工創建一下,並設置好讀寫許可權。
如果需要在代碼中,更容易控制堆棧的輸出時機,可以用以下命令獲取zygote的core mp:
[java] view plain
Process.sendSignal(pid, Process.SIGNAL_QUIT);
原理和命令行是一樣的。
不過需要注意兩點:
adb shell可能會沒有許可權,需要root。
android 4.2中關閉了native thread的堆棧列印,詳見 dalvik/vm/Thread.cpp的mpNativeThread方法:
[cpp] view plain
dvmPrintDebugMessage(target,
"\"%s\" sysTid=%d nice=%d sched=%d/%d cgrp=%s\n",
name, tid, getpriority(PRIO_PROCESS, tid),
schedStats.policy, schedStats.priority, schedStats.group);
mpSchedStat(target, tid);
// Temporarily disabled collecting native stacks from non-Dalvik
// threads because sometimes they misbehave.
//dvmDumpNativeStack(target, tid);
Native堆棧的列印被關掉了!不過對於大多數情況,可以直接將這個注釋打開。

2. debuggerd的堆棧mp
debuggerd是android的一個daemon進程,負責在進程異常出錯時,將進程的運行時信息mp出來供分析。debuggerd生 成的coremp數據是以文本形式呈現,被保存在 /data/tombstone/ 目錄下(名字取的也很形象,tombstone是墓碑的意思),共可保存10個文件,當超過10個時,會覆蓋重寫最早生成的文件。從4.2版本開 始,debuggerd同時也是一個實用工具:可以在不中斷進程執行的情況下列印當前進程的native堆棧。使用方法是:
[plain] view plain
debuggerd -b <pid>
這可以協助我們分析進程執行行為,但最最有用的地方是:它可以非常簡單的定位到native進程中鎖死或錯誤邏輯引起的死循環的代碼位置。

3. java代碼中列印堆棧
Java代碼列印堆棧比較簡單, 堆棧信息獲取和輸出,都可以通過Throwable類的方法實現。目前通用的做法是在java進程出現需要注意的異常時,列印堆棧,然後再決定退出或挽救。通常的方法是使用exception的printStackTrace()方法:
[java] view plain
try {
...
} catch (RemoteException e) {
e.printStackTrace();
...
}
當然也可以只列印堆棧不退出,這樣就比較方便分析代碼的動態運行情況。Java代碼中插入堆棧列印的方法如下:
[java] view plain
Log.d(TAG,Log.getStackTraceString(new Throwable()));

4. C++代碼中列印堆棧
C++也是支持異常處理的,異常處理庫中,已經包含了獲取backtrace的介面,Android也是利用這個介面來列印堆棧信息的。在Android的C++中,已經集成了一個工具類CallStack,在libutils.so中。使用方法:
[cpp] view plain
#include <utils/CallStack.h>
...
CallStack stack;
stack.update();
stack.mp();
使用方式比較簡單。目前Andoid4.2版本已經將相關信息解析的很到位,符號表查找,demangle,偏移位置校正都做好了。
[plain] view plain

5. C代碼中列印堆棧
C代碼,尤其是底層C庫,想要看到調用的堆棧信息,還是比較麻煩的。 CallStack肯定是不能用,一是因為其實C++寫的,需要重新封裝才能在C中使用,二是底層庫反調上層庫的函數,會造成鏈接器循環依賴而無法鏈接。 不過也不是沒有辦法,可以通過android工具類CallStack實現中使用的unwind調用及符號解析函數來處理。
這里需要注意的是,為解決鏈接問題,最好使用dlopen方式,查找需要用到的介面再直接調用,這樣會比較簡單。如下為相關的實現代碼,只需要在要 列印的文件中插入此部分代碼,然後調用getCallStack()即可,無需包含太多的頭文件和修改Android.mk文件:
[cpp] view plain
#define MAX_DEPTH 31
#define MAX_BACKTRACE_LINE_LENGTH 800
#define PATH "/system/lib/libcorkscrew.so"

typedef ssize_t (*unwindFn)(backtrace_frame_t*, size_t, size_t);
typedef void (*unwindSymbFn)(const backtrace_frame_t*, size_t, backtrace_symbol_t*);
typedef void (*unwindSymbFreeFn)(backtrace_symbol_t*, size_t);

static void *gHandle = NULL;

static int getCallStack(void){
ssize_t i = 0;
ssize_t result = 0;
ssize_t count;
backtrace_frame_t mStack[MAX_DEPTH];
backtrace_symbol_t symbols[MAX_DEPTH];

unwindFn unwind_backtrace = NULL;
unwindSymbFn get_backtrace_symbols = NULL;
unwindSymbFreeFn free_backtrace_symbols = NULL;

// open the so.
if(gHandle == NULL) gHandle = dlopen(PATH, RTLD_NOW);

// get the interface for unwind and symbol analyse
if(gHandle != NULL) unwind_backtrace = (unwindFn)dlsym(gHandle, "unwind_backtrace");
if(gHandle != NULL) get_backtrace_symbols = (unwindSymbFn)dlsym(gHandle, "get_backtrace_symbols");
if(gHandle != NULL) free_backtrace_symbols = (unwindSymbFreeFn)dlsym(gHandle, "free_backtrace_symbols");

if(!gHandle ||!unwind_backtrace ||!get_backtrace_symbols || !free_backtrace_symbols ){
ALOGE("Error! cannot get unwind info: handle:%p %p %p %p",
gHandle, unwind_backtrace, get_backtrace_symbols, free_backtrace_symbols );
return result;
}

count= unwind_backtrace(mStack, 1, MAX_DEPTH);
get_backtrace_symbols(mStack, count, symbols);

for (i = 0; i < count; i++) {
char line[MAX_BACKTRACE_LINE_LENGTH];

const char* mapName = symbols[i].map_name ? symbols[i].map_name : "<unknown>";
const char* symbolName =symbols[i].demangled_name ? symbols[i].demangled_name : symbols[i].symbol_name;
size_t fieldWidth = (MAX_BACKTRACE_LINE_LENGTH - 80) / 2;

if (symbolName) {
uint32_t pc_offset = symbols[i].relative_pc - symbols[i].relative_symbol_addr;
if (pc_offset) {
snprintf(line, MAX_BACKTRACE_LINE_LENGTH, "#%02d pc %08x %.*s (%.*s+%u)",
i, symbols[i].relative_pc, fieldWidth, mapName,
fieldWidth, symbolName, pc_offset);
} else {
snprintf(line, MAX_BACKTRACE_LINE_LENGTH, "#%02d pc %08x %.*s (%.*s)",
i, symbols[i].relative_pc, fieldWidth, mapName,
fieldWidth, symbolName);
}
} else {
snprintf(line, MAX_BACKTRACE_LINE_LENGTH, "#%02d pc %08x %.*s",
i, symbols[i].relative_pc, fieldWidth, mapName);
}

ALOGD("%s", line);
}

free_backtrace_symbols(symbols, count);

return result;
}
對sched_policy.c的堆棧調用分析如下,注意具體是否要列印,在哪裡列印,還可以通過pid、uid、property等來控制一下,這樣就不會被淹死在trace的汪洋大海中。
[plain] view plain
D/SchedPolicy( 1350): #00 pc 0000676c /system/lib/libcutils.so
D/SchedPolicy( 1350): #01 pc 00006b3a /system/lib/libcutils.so (set_sched_policy+49)
D/SchedPolicy( 1350): #02 pc 00010e82 /system/lib/libutils.so (androidSetThreadPriority+61)
D/SchedPolicy( 1350): #03 pc 00068104 /system/lib/libandroid_runtime.so (android_os_Process_setThreadPriority(_JNIEnv*, _jobject*, int, int)+7)
D/SchedPolicy( 1350): #04 pc 0001e510 /system/lib/libdvm.so (dvmPlatformInvoke+112)
D/SchedPolicy( 1350): #05 pc 0004d6aa /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*)+417)
D/SchedPolicy( 1350): #06 pc 00027920 /system/lib/libdvm.so
D/SchedPolicy( 1350): #07 pc 0002b7fc /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JValue*)+184)
D/SchedPolicy( 1350): #08 pc 00060c30 /system/lib/libdvm.so (dvmCallMethodV(Thread*, Method const*, Object*, bool, JValue*, std::__va_list)+271)
D/SchedPolicy( 1350): #09 pc 0004cd34 /system/lib/libdvm.so
D/SchedPolicy( 1350): #10 pc 00049382 /system/lib/libandroid_runtime.so
D/SchedPolicy( 1350): #11 pc 00065e52 /system/lib/libandroid_runtime.so
D/SchedPolicy( 1350): #12 pc 0001435e /system/lib/libbinder.so (android::BBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+57)
D/SchedPolicy( 1350): #13 pc 00016f5a /system/lib/libbinder.so (android::IPCThreadState::executeCommand(int)+513)
D/SchedPolicy( 1350): #14 pc 00017380 /system/lib/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+183)
D/SchedPolicy( 1350): #15 pc 0001b160 /system/lib/libbinder.so
D/SchedPolicy( 1350): #16 pc 00011264 /system/lib/libutils.so (android::Thread::_threadLoop(void*)+111)
D/SchedPolicy( 1350): #17 pc 000469bc /system/lib/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+63)
D/SchedPolicy( 1350): #18 pc 00010dca /system/lib/libutils.so
D/SchedPolicy( 1350): #19 pc 0000e3d8 /system/lib/libc.so (__thread_entry+72)
D/SchedPolicy( 1350): #20 pc 0000dac4 /system/lib/libc.so (pthread_create+160)
D/SchedPolicy( 1350): #00 pc 0000676c /system/lib/libcutils.so
D/SchedPolicy( 1350): #01 pc 00006b3a /system/lib/libcutils.so (set_sched_policy+49)
D/SchedPolicy( 1350): #02 pc 00016f26 /system/lib/libbinder.so (android::IPCThreadState::executeCommand(int)+461)
D/SchedPolicy( 1350): #03 pc 00017380 /system/lib/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+183)
D/SchedPolicy( 1350): #04 pc 0001b160 /system/lib/libbinder.so
D/SchedPolicy( 1350): #05 pc 00011264 /system/lib/libutils.so (android::Thread::_threadLoop(void*)+111)
D/SchedPolicy( 1350): #06 pc 000469bc /system/lib/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+63)
D/SchedPolicy( 1350): #07 pc 00010dca /system/lib/libutils.so
D/SchedPolicy( 1350): #08 pc 0000e3d8 /system/lib/libc.so (__thread_entry+72)
D/SchedPolicy( 1350): #09 pc 0000dac4 /system/lib/libc.so (pthread_create+160)

6. 其它堆棧信息查詢

㈣ Android Native庫的載入及動態鏈接

我們從一個簡單的NDK Demo開始分析。

下面從 System.loadLibrary() 開始分析。

下面看 loadLibrary0()

參數 loader 為Android的應用類載入器,它是 PathClassLoader 類型的對象,繼承自 BaseDexClassLoader 對象,下面看 BaseDexClassLoader 的 findLibrary() 方法。

下面看 DexPathList 的 findLibrary() 方法

回到 loadLibrary0() ,有了動態庫的全路徑名就可以裝載庫了,下面看 doLoad() 。

nativeLoad() 最終調用 LoadNativeLibrary() ,下面直接分析 LoadNativeLibrary() 。

對於JNI注冊,這里暫不討論,下面看 OpenNativeLibrary() 的實現。

下面看 android_dlopen_ext() 的實現

接下來就Android鏈接器linker的工作了。

下面從 do_dlopen() 開始分析。

find_library() 當參數translated_name不為空時,直接調用 find_libraries() ,這是裝載鏈接的關鍵函數,下面看它的實現。

find_libraries() 中動態庫的裝載可以分為兩部分

下面從 find_library_internal() 開始分析。

下面分析 load_library()

下面看另一個 load_library() 的實現

下面分析ELF文件頭以及段信息的讀取過程,也就是LoadTask的 read() ,它直接調用ElfReader的 Read() 方法。

動態庫的裝載在LoadTask的 load() 中實現。

下面看ElfReader的 Load() 方法

動態庫的裝載已經完成,下面看鏈接過程。

下面看 prelink_image()

鏈接主要完成符號重定位工作,下面從 link_image() 開始分析

下面以函數引用重定位為例分析 relocate() 方法

㈤ android注入so怎麼使用

dhrurthr64uhtdh

㈥ Android UART 串口通信

最近有項目需要實現windows機器和Android開發版進行UART串口通信,經過3天查找嘗試,特記錄一下最終方案,希望之後的同行少走彎路,最後在git上回開源我最終的方案希望大家支持。

Android 3.0.1
Gradle 4.1
ARM開發版 : RK3399
PC機器:Win10
開發機器:MAC 10.13.3

先上圖

由於 android-serialport-api 項目中的so使用較old的ndk編譯,所以在對於Android 6.0 以上版本兼容的時候會報錯 dlopen failed: "has text relocations" 。且使用的mk進行編譯,特升級為用cmake編譯。

升級 android-serialport-api

項目結構:

app對應原項目中的各個Activity, androidserial 是mole 對應編譯之前的so,還有API的封裝。可以直接引用androidserial,調用方法參考app目錄下的activity。

注意 關於許可權!

當接入開發板後如果發現 Error You do not have read/write permission to the serial port 需要root 許可權 ,在開發者模式中開啟root 許可權 adb和應用

使用一下命令開啟Android對串口的讀寫許可權

setenforce 0 : 關閉防火牆,有人說關鍵是這,但是我的環境不用關閉,只要給許可權就可以

注意 關於ttyS1 - 6 ttyS1 - 6 對應的是 UART 串口1-6 一般都是一一對應的。這個具體要看一下開發板的說明。

記錄的比較糙,還請見諒,如有問題請留言,我看到後肯定回復。項目主要看結構,剩下的都是復制黏貼的事。 git地址:https://github.com/braincs/AndroidSerialLibrary

㈦ 安卓4.4.2系統使用sqlcipher時,出現dlopen failed: cannot locate symbol referenced by...

好像是路徑錯了。

㈧ 在Android中怎麼使用system/lib下的庫中的函數

android.mk
LOCAL_LDLIBS := -llog -lz 像這樣-l加庫名稱
如果沒頭文件
看是否可此嘩畝以動態調用
例如:

void (*ucnv_convert)(const char *,const char *, char * , int32_t , const char *, int32_t,int32_t*)=0;

void* pDL = dlopen("/system/lib/libicuuc.so", RTLD_LAZY);

ucnv_convert = (void (*)(const char *, const char *, char * , int32_t , const char *, int32_t,int32_t*))dlsym(pDL, "ucnv_convert_44");

ucnv_convert 就可以蘆亂當函數森森使用了

熱點內容
javaapi源碼 發布:2024-04-25 07:51:15 瀏覽:605
怎麼在伺服器執行jmeter腳本 發布:2024-04-25 07:35:25 瀏覽:396
域名訪問https 發布:2024-04-25 07:16:56 瀏覽:414
javaie亂碼 發布:2024-04-25 07:07:15 瀏覽:602
php開發微信支付 發布:2024-04-25 06:57:38 瀏覽:317
上傳視頻最快 發布:2024-04-25 06:42:59 瀏覽:14
允許更新預編譯站點 發布:2024-04-25 06:32:53 瀏覽:679
如何獲取已經連上的網路密碼 發布:2024-04-25 06:27:48 瀏覽:466
python打開界面 發布:2024-04-25 06:27:44 瀏覽:667
java數組重復 發布:2024-04-25 06:27:40 瀏覽:827