diff --git a/compiler/systems/t_android.pas b/compiler/systems/t_android.pas index 79a09ba14b..d1852461c0 100644 --- a/compiler/systems/t_android.pas +++ b/compiler/systems/t_android.pas @@ -36,8 +36,13 @@ interface procedure generatelib;override; end; + { texportlibandroid } + texportlibandroid=class(texportlibunix) + public procedure setfininame(list: TAsmList; const s: string); override; + procedure exportprocedure(hp: texported_item); override; + procedure generatelib; override; end; { tlinkerandroid } @@ -46,6 +51,7 @@ interface private prtobj : string[80]; reorder : boolean; + FJNIOnLoadDef: tprocdef; Function WriteResponseFile(isdll:boolean) : Boolean; function DoLink(IsSharedLib: boolean): boolean; public @@ -66,12 +72,15 @@ implementation verbose,systems,globtype,globals, symconst,script, fmodule, - aasmbase,aasmtai,aasmcpu,cpubase, + aasmbase,aasmtai,aasmcpu,cpubase,hlcgcpu,hlcgobj, cgbase,cgobj,cgutils,ogbase,ncgutil, comprsrc, rescmn, i_android ; +const + SJNI_OnLoad = 'JNI_OnLoad'; + {***************************************************************************** TIMPORTLIBANDROID *****************************************************************************} @@ -103,6 +112,44 @@ implementation inherited setfininame(list,s); end; + procedure texportlibandroid.exportprocedure(hp: texported_item); + begin + { + Android versions prior to 4.1 do not support recursive dlopen() calls. + Therefore if a shared library is loaded by JVM ( using dlopen() ), + it is not possible to use dlopen() in a units initialization code - + dlopen() simply hangs. + To workaround this issue, if a library exports JNI_OnLoad(), then + no unit initialization is performed during library load. + The initialization is called when JVM has loaded the library and calls + JNI_OnLoad(). + } + // Check for the JNI_OnLoad export + if current_module.islibrary and not hp.is_var and assigned(hp.sym) and + (hp.sym.typ = procsym) and (eo_name in hp.options) and + (hp.name^ = SJNI_OnLoad) and (tprocsym(hp.sym).procdeflist.count = 1) then + begin + // Save the JNI_OnLoad procdef + tlinkerandroid(Linker).FJNIOnLoadDef:=tprocdef(tprocsym(hp.sym).procdeflist[0]);; + hp.Free; + exit; + end; + inherited exportprocedure(hp); + end; + + procedure texportlibandroid.generatelib; + begin + inherited generatelib; + if tlinkerandroid(Linker).FJNIOnLoadDef = nil then + exit; + // If JNI_OnLoad is exported, export a system proxy function instead + create_hlcodegen; + new_section(current_asmdata.asmlists[al_procedures],sec_code,'',0); + hlcg.g_external_wrapper(current_asmdata.asmlists[al_procedures],nil,SJNI_OnLoad,'FPC_JNI_ON_LOAD_PROXY',true); + destroy_hlcodegen; + exportedsymnames.insert(SJNI_OnLoad); + end; + {***************************************************************************** TLINKERANDROID *****************************************************************************} @@ -304,6 +351,20 @@ begin add('}'); add('INSERT BEFORE .data1'); + // Define different aliases for normal and JNI libraries + if FJNIOnLoadDef <> nil then + begin + s:=FJNIOnLoadDef.mangledname; + s1:='FPC_JNI_LIB_MAIN_ANDROID'; + end + else + begin + s:='0'; + s1:='PASCALMAIN'; + end; + add('FPC_JNI_ON_LOAD = ' + s + ';'); + add('FPC_LIB_MAIN_ANDROID = ' + s1 + ';'); + { Write and Close response } writetodisk; Free; diff --git a/rtl/android/arm/dllprt0.as b/rtl/android/arm/dllprt0.as index 87e0157306..93e48609d3 100644 --- a/rtl/android/arm/dllprt0.as +++ b/rtl/android/arm/dllprt0.as @@ -47,7 +47,7 @@ FPC_SHARED_LIB_START: str r0,[ip] /* Call main */ - blx PASCALMAIN + blx FPC_LIB_MAIN_ANDROID /* Call library init */ blx FPC_LIB_INIT_ANDROID diff --git a/rtl/android/i386/dllprt0.as b/rtl/android/i386/dllprt0.as index e6b3abf27b..ae48292c5d 100644 --- a/rtl/android/i386/dllprt0.as +++ b/rtl/android/i386/dllprt0.as @@ -51,7 +51,7 @@ env_ok: popl %ebx /* Call main */ - call PASCALMAIN@PLT + call FPC_LIB_MAIN_ANDROID@PLT /* Call library init */ call FPC_LIB_INIT_ANDROID@PLT diff --git a/rtl/android/mipsel/dllprt0.as b/rtl/android/mipsel/dllprt0.as index 4a0302c04a..5237bbf0c7 100644 --- a/rtl/android/mipsel/dllprt0.as +++ b/rtl/android/mipsel/dllprt0.as @@ -44,7 +44,7 @@ GotEnv: sw $t0, ($t1) /* Call main */ - jal PASCALMAIN + jal FPC_LIB_MAIN_ANDROID nop /* Call library init */ jal FPC_LIB_INIT_ANDROID diff --git a/rtl/android/sysandroid.inc b/rtl/android/sysandroid.inc index 26e26f2d12..33272de347 100644 --- a/rtl/android/sysandroid.inc +++ b/rtl/android/sysandroid.inc @@ -49,7 +49,7 @@ begin { Starting from Android 4.4 stdio handles are closed by libc prior to calling finalization routines of shared libraries. This causes a error while trying to writeln during library finalization and finally a crash because the error can - not be printer too. + not be printed too. It is needed to save stdout and stderr handles by duplicating them and restore them before library finalization. } @@ -206,12 +206,34 @@ begin DefaultLogTag[len + 1]:=#0; end; +// ************* JNI init + +function JNI_OnLoad_Real(vm: pointer; reserved: pointer): longint;{$ifdef windows} stdcall {$else} cdecl {$endif}; external name 'FPC_JNI_ON_LOAD'; +procedure PascalMain; external name 'PASCALMAIN'; + +// This proxy function is called when JVM calls the JNI_OnLoad() exported function +function JNI_OnLoad_Proxy(vm: pointer; reserved: pointer): longint;{$ifdef windows} stdcall {$else} cdecl {$endif}; [public, alias:'FPC_JNI_ON_LOAD_PROXY']; +begin + IsJniLibrary:=True; + // Call library initialization + PascalMain; + // Call user's JNI_OnLoad(). + Result:=JNI_OnLoad_Real(vm, reserved); +end; + +// This procedure is called instead of library initialization when JNI_OnLoad is exported +procedure JniLibMain; [public, alias:'FPC_JNI_LIB_MAIN_ANDROID']; +begin + // Must be empty. +end; + +// ************* System init + procedure InitAndroid; var i: integer; s: string; begin - IsJniLibrary:=IsLibrary and (Pos('/system/', ParamStr(0)) = 1); if IsJniLibrary then begin // The library is loaded by a Java app. The proper tag will be set by SysUtils.