Parcourir la source

* android: 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().

git-svn-id: trunk@34406 -

yury il y a 9 ans
Parent
commit
a0d6873331

+ 62 - 1
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;

+ 1 - 1
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
 

+ 1 - 1
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
 

+ 1 - 1
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

+ 24 - 2
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.