歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Dalvik——Libffi學習文檔

0 序言
Libffi源碼位於/Android2.1/external/libffi,它本身是一個開源項目,用於高級語言之間的相互調用的處理,在Android中作為JAVA的本地調用的JNI Call Bridge的一種實現方式(默認對於未知架構才會調用)。
第一部分介紹了Libffi自身的一些實現機制和使用方法,主要翻譯了/android2.1/external/libffi/docs/info.txt
1 Libffi實現機制
1.1 Libffi介紹
高級語言的編譯器根據一定的規則生成代碼,這些規則對於不同編譯器的工作是必須的。其中一個規則叫做“調用規則”(Calling Convention),它包含了編譯器關於在函數入口處函數參數位置、函數返回值位置的一系列假設。它有時也被稱作“ABI”(Application Binary Interface)。
一些程序在編譯時可能不知道傳給函數的參數是什麼,例如,解釋器在運行時才被告知調用給定函數的參數類型和數量。Libffi可以在這些函數中看做是連接要解釋的函數和編譯後的代碼之間的橋梁。
“Libffi”庫為多種調用規則提供了可移植的高級語言編程接口,這使得程序員在運行時可以調用任何由調用接口指定的函數。
FFI(Foreign Function Interface)允許以一種語言編寫的代碼調用另一種語言的代碼,而Libffi庫提供了最底層的、與架構相關的、完整的FFI,因此在它的上層必須有函數來管理兩種語言之間參數的格式轉換。

1.2 如何使用FFI
1.2.1 FFI使用流程
Libffi假設你有要調用的函數的指針、要傳遞的參數的類型和數量以及該函數返回值的類型。
第一步你必須創建與你要調用的函數特征相匹配的“ffi_cif”對象,這是單獨的一步因為多次調用一個“ffi_cif”對象很常見(其中的“cif”指的是Call Interface)。要創建“ffi_cif”對象必須調用函數“ffi_prep_cif”。

聲明位置:android2.1/external/libffi/include/ffi_real.h    Line 348
ffi_status ffi_prep_cif(ffi_cif *cif,
    ffi_abi abi,
    unsigned int nargs,
    ffi_type *rtype,
    ffi_type **atypes);
實現位置:android/external/Libffi/src/prep_cif.c    Line 88
ffi_prep_cif(ffi_cif *cif, ffi_abi abi, unsigned int nargs,ffi_type *rtype, ffi_type **atypes)
參數解釋:
abi:要使用的ABI,通常使用FFI_DEFAULT_ABI就可以了;
nargs:函數接受的參數的數量;
rtype:指向FFI_TYPE結構體的指針,描述函數的返回類型;
argtypes:FFI_TYPE結構體的向量,其中必須包含nargs元素。
ffi_prep_cif返回“ffi_status”類型變量,如果初始化正確則返回“FFI_OK”,如果一個“ffi_type”對象錯誤則返回“FFI_BAD_TYPEDEF”,如果ABI參數錯誤則返回“FFI_BAD_ABI”。
第二步你必須調用“ffi_call”函數來調用初始化的“ffi_cif”對象。

聲明位置:
android2.1/external/libffi/include/ffi_real.h    Line 354
void ffi_call(ffi_cif *cif,
       void (*fn)(void),
       void *rvalue,
       void **avalue);
實現位置:由android2.1/external/libffi/src下各架構中ffi.c實現
參數解釋:
cif:由“ffi_prep_cif”指定的cif;
fn:根據cif的不同調用函數fn;
rvalue:一塊內存的指針,存放函數調用返回的結果。它必須足夠大並且合理分配,由調用者負責確保這一點;
avalue:一個“void *”指針用來指向保存調用函數參數的存放地址。

2、Android2.1 Libffi的實現
2.1 FFI的實現
在Android2.1中Libffi整體作為外部文件放在/external/libffi實現,其目錄架構如下:
目錄:/android2.1/external/libffi/
主要結構分析:
-doc    libffi的文檔管理。
--libffi.info   libffi的介紹;
--libffi.texi   定義了libffi需要的部分API,可以看成是定義了部分全局變量;
--stamp-vti   (?)版本管理的記錄;
--version.texi   同上;
-include   頭文件。
--ffi.h.in    定義了libffi需要的部分API,可以看成是定義了部分全局變量;
--ffi_common.h 定義了編譯libffi必須的內部變量和宏;
--ffi_real.h   內容幾乎同ffi.h.in,僅將與架構相關的幾行重新另寫為ffitarget.h放
在/android2.1/external/libffi/src/各個架構下面;
--Makefile.am   Makefile文件,指定頭文件的編譯過程,由上層make調用;
--Makefile.in   Makefile.am生成;
-linux-arm arm架構的頭文件。
--ffi.h    arm架構下ffi所需的頭文件;
--fficonfig.h   arm架構下ffi配置文件,定義了一些宏;
-linux-x86 x86架構的頭文件。
--ffi.h    x86架構下ffi所需的頭文件(和arm下幾乎一樣);
--fficonfig.h   x86架構下ffi配置文件,定義了一些宏(和arm下幾乎一樣,多定
      義了一些宏);
-man   (?)重要函數幾乎的機器語言解釋。
--ffi.3    初始化ffi的機器級解釋;
--ffi_call.3   ffi_call函數的機器級解釋;
--ffi_prep_cif.3   ffi_prep_cif函數的機器級解釋;
--Makefile.am   Makefile文件,指定編譯方法,由上層make調用;
--Makefile.in   由Makefile.am生成;
-src    不同架構下ffi的實現,包括alpha、arm、cris、frv、IA64、M32R、M68K、MIPS、
PA、powerpc、s390、SH、SH64、sparc和X86架構,每個架構有單獨的文件夾,下面僅分析arm、x86架構文件夾。
--arm
   ---ffi.c   實現ffi的基本函數的實現;
   --ffitarget.h 頭文件,在/…/linux-arm/ffi.h中調用;
   --sysv.S   定義了函數ffi_closure_SYSV、ffi_call_SYSV等,實際完成ffi_call等函
數功能;
--x86
   --ffi.c   實現ffi的基本函數的實現;
   --ffitarget.h 頭文件,在/…/linux-arm/ffi.h中調用;
   --sysv.S   定義了函數ffi_closure_SYSV、ffi_call_SYSV等,實際完成ffi_call等函
數功能;
   --其它文件為*.S,為Darwin、UNIX64、WIN32、FREEBSD操作系統下ffi的匯編實現。
--prep_cif.c   准備好函數的cif;
--closures.c   定義了Closure API相關的方法;
--dlmalloc.c   與內存分配、malloc相關的東西;
--types.c    定義了ffi需要的TYPE;
--raw_api.c   定義了Raw API相關的方法;
--java_raw_api.c 定義了仿Java的Raw API相關的方法,和raw API差不多;
--debug.c    debug ffi時需要的方法;
-testsuite   (?)測試用的方法,暫時未看。
-Android.mk Makefile文件,只定義了arm和x86架構的編譯流程,生成libffi庫文件。
-ChangeLog.* 修改日志,可以便於理解。
-compile   編譯器需要的,解釋-c、-o命令。
-configure.ac 用來生成configure執行文件。
-Makefile.am 整個libffi的編譯規則,生成Makefile.in。

其中raw API和closure API是用來傳參、封裝方法的接口:raw API是用來繞過由架構封裝或者未封裝的參數;closure API將解釋過的函數封裝進C指針,從而可以作為C函數來看,這也可以用在將用戶參數和函數指針封裝到一個函數指針裡面(這正是ffi要做的,因此closure API一定要實現)。

2.2 Android Libffi調用
在/android2.1/dalvik/vm/Dvm.mk中有以下語句:

ifeq ($(MTERP_ARCH_KNOWN),false)
# unknown architecture, try to use FFI
LOCAL_C_INCLUDES += external/libffi/$(dvm_os)-$(dvm_arch)
LOCAL_SHARED_LIBRARIES += libffi

LOCAL_SRC_FILES += \
   arch/generic/Call.c \
   arch/generic/Hints.c \
   mterp/out/InterpC-allstubs.c
在android2.1裡,對於未知架構android將調用libffi來處理不同語言的函數調用問題,調用的源文件位於/android2.1/dalvik/vm/arch/generic/Calls.c和Hints.c;而對於確定的架構(例如ARM、X86、MIPS等)則采用他們架構相關的方法來處理該問題。

Copyright © Linux教程網 All Rights Reserved