* 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 -
This commit is contained in:
yury 2016-09-01 23:22:41 +00:00
parent 649823a246
commit a0d6873331
5 changed files with 89 additions and 6 deletions

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.