فهرست منبع

* 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 9 سال پیش
والد
کامیت
a0d6873331
5فایلهای تغییر یافته به همراه89 افزوده شده و 6 حذف شده
  1. 62 1
      compiler/systems/t_android.pas
  2. 1 1
      rtl/android/arm/dllprt0.as
  3. 1 1
      rtl/android/i386/dllprt0.as
  4. 1 1
      rtl/android/mipsel/dllprt0.as
  5. 24 2
      rtl/android/sysandroid.inc

+ 62 - 1
compiler/systems/t_android.pas

@@ -36,8 +36,13 @@ interface
       procedure generatelib;override;
       procedure generatelib;override;
     end;
     end;
 
 
+    { texportlibandroid }
+
     texportlibandroid=class(texportlibunix)
     texportlibandroid=class(texportlibunix)
+    public
       procedure setfininame(list: TAsmList; const s: string); override;
       procedure setfininame(list: TAsmList; const s: string); override;
+      procedure exportprocedure(hp: texported_item); override;
+      procedure generatelib; override;
     end;
     end;
 
 
     { tlinkerandroid }
     { tlinkerandroid }
@@ -46,6 +51,7 @@ interface
     private
     private
       prtobj  : string[80];
       prtobj  : string[80];
       reorder : boolean;
       reorder : boolean;
+      FJNIOnLoadDef: tprocdef;
       Function  WriteResponseFile(isdll:boolean) : Boolean;
       Function  WriteResponseFile(isdll:boolean) : Boolean;
       function DoLink(IsSharedLib: boolean): boolean;
       function DoLink(IsSharedLib: boolean): boolean;
     public
     public
@@ -66,12 +72,15 @@ implementation
     verbose,systems,globtype,globals,
     verbose,systems,globtype,globals,
     symconst,script,
     symconst,script,
     fmodule,
     fmodule,
-    aasmbase,aasmtai,aasmcpu,cpubase,
+    aasmbase,aasmtai,aasmcpu,cpubase,hlcgcpu,hlcgobj,
     cgbase,cgobj,cgutils,ogbase,ncgutil,
     cgbase,cgobj,cgutils,ogbase,ncgutil,
     comprsrc,
     comprsrc,
     rescmn, i_android
     rescmn, i_android
     ;
     ;
 
 
+const
+  SJNI_OnLoad = 'JNI_OnLoad';
+
 {*****************************************************************************
 {*****************************************************************************
                                TIMPORTLIBANDROID
                                TIMPORTLIBANDROID
 *****************************************************************************}
 *****************************************************************************}
@@ -103,6 +112,44 @@ implementation
         inherited setfininame(list,s);
         inherited setfininame(list,s);
       end;
       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
                                   TLINKERANDROID
 *****************************************************************************}
 *****************************************************************************}
@@ -304,6 +351,20 @@ begin
       add('}');
       add('}');
       add('INSERT BEFORE .data1');
       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 }
       { Write and Close response }
       writetodisk;
       writetodisk;
       Free;
       Free;

+ 1 - 1
rtl/android/arm/dllprt0.as

@@ -47,7 +47,7 @@ FPC_SHARED_LIB_START:
         str r0,[ip]
         str r0,[ip]
 
 
         /* Call main */
         /* Call main */
-        blx PASCALMAIN
+        blx FPC_LIB_MAIN_ANDROID
         /* Call library init */
         /* Call library init */
         blx FPC_LIB_INIT_ANDROID
         blx FPC_LIB_INIT_ANDROID
 
 

+ 1 - 1
rtl/android/i386/dllprt0.as

@@ -51,7 +51,7 @@ env_ok:
         popl    %ebx
         popl    %ebx
 
 
         /* Call main */
         /* Call main */
-        call    PASCALMAIN@PLT
+        call    FPC_LIB_MAIN_ANDROID@PLT
         /* Call library init */
         /* Call library init */
         call    FPC_LIB_INIT_ANDROID@PLT
         call    FPC_LIB_INIT_ANDROID@PLT
 
 

+ 1 - 1
rtl/android/mipsel/dllprt0.as

@@ -44,7 +44,7 @@ GotEnv:
     sw $t0, ($t1)
     sw $t0, ($t1)
 
 
     /* Call main */
     /* Call main */
-    jal PASCALMAIN
+    jal FPC_LIB_MAIN_ANDROID
     nop
     nop
     /* Call library init */
     /* Call library init */
     jal FPC_LIB_INIT_ANDROID
     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
   { 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
     finalization routines of shared libraries. This causes a error while trying to
     writeln during library finalization and finally a crash because the error can
     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
     It is needed to save stdout and stderr handles by duplicating them and restore
     them before library finalization.
     them before library finalization.
   }
   }
@@ -206,12 +206,34 @@ begin
   DefaultLogTag[len + 1]:=#0;
   DefaultLogTag[len + 1]:=#0;
 end;
 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;
 procedure InitAndroid;
 var
 var
   i: integer;
   i: integer;
   s: string;
   s: string;
 begin
 begin
-  IsJniLibrary:=IsLibrary and (Pos('/system/', ParamStr(0)) = 1);
   if IsJniLibrary then
   if IsJniLibrary then
     begin
     begin
       // The library is loaded by a Java app. The proper tag will be set by SysUtils.
       // The library is loaded by a Java app. The proper tag will be set by SysUtils.