Skip to content

Commit 2c84d57

Browse files
authored
Merge pull request #351 from Embarcadero/scanlibs
Scanning loaded libraries looking for Python symbols
2 parents d271451 + 8252793 commit 2c84d57

File tree

1 file changed

+163
-13
lines changed

1 file changed

+163
-13
lines changed

Source/PythonEngine.pas

Lines changed: 163 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,8 +1197,13 @@ TDynamicDll = class(TComponent)
11971197
procedure DoOpenDll(const aDllName : string); virtual;
11981198
function GetDllPath : string;
11991199

1200+
procedure LoadPythonInfoFromModule;
1201+
function GetPythonModuleFromProcess(): NativeUInt;
12001202
function HasHostSymbols(): boolean;
12011203
procedure LoadFromHostSymbols();
1204+
//Loading strategies
1205+
function TryLoadFromHostSymbols(): boolean;
1206+
function TryLoadFromCurrentProcess(): boolean;
12021207
public
12031208
// Constructors & Destructors
12041209
constructor Create(AOwner: TComponent); override;
@@ -2783,6 +2788,7 @@ implementation
27832788
{$ENDIF}
27842789
{$IFDEF MSWINDOWS}
27852790
Registry,
2791+
PsAPI,
27862792
{$ENDIF}
27872793
Math;
27882794

@@ -3019,6 +3025,123 @@ function TDynamicDll.GetDllPath : string;
30193025
end;
30203026
end;
30213027

3028+
function TDynamicDll.GetPythonModuleFromProcess(): NativeUInt;
3029+
3030+
{$IFNDEF FPC}
3031+
3032+
function HasSymbols(const AModule: NativeUInt): boolean;
3033+
var
3034+
LPy_GetBuildInfo: function : PAnsiChar; cdecl;
3035+
LPy_IsInitialized: function: integer; cdecl;
3036+
begin
3037+
FDLLHandle := AModule;
3038+
try
3039+
LPy_GetBuildInfo := Import('Py_GetBuildInfo', false);
3040+
LPy_IsInitialized := Import('Py_IsInitialized', false);
3041+
Result := Assigned(LPy_GetBuildInfo) and Assigned(LPy_GetBuildInfo())
3042+
and Assigned(LPy_IsInitialized) and (LPy_IsInitialized() <> 0);
3043+
finally
3044+
FDLLHandle := 0;
3045+
end;
3046+
end;
3047+
3048+
{$IFDEF LINUX}
3049+
function GetPythonModule: NativeUInt;
3050+
type
3051+
plink_map = ^link_map;
3052+
link_map = record
3053+
l_addr: Pointer;
3054+
l_name: PAnsiChar;
3055+
l_ld: Pointer;
3056+
l_next, l_prev: plink_map;
3057+
end;
3058+
var
3059+
LPseudoHandle: NativeUInt;
3060+
LPLinkMap: plink_map;
3061+
LModuleName: string;
3062+
LModuleHandle: NativeUInt;
3063+
begin
3064+
//In Linux pseudo handle is in fact a pointer to the the corresponding link_map structure
3065+
//The dlopen(nil, RTLD_NOW) result is the pseudo handle for the main executable (similar to GetModuleHandle(nil) in Windows).
3066+
LPseudoHandle := dlopen(nil, RTLD_NOW);
3067+
//Points to the first link_map
3068+
LPLinkMap := plink_map(LPseudoHandle).l_next.l_next;
3069+
while Assigned(LPLinkMap) do begin
3070+
LModuleName := String(LPLinkMap.l_name);
3071+
LModuleHandle := LoadLibrary(PChar(LModuleName));
3072+
if HasSymbols(LModuleHandle) then
3073+
Exit(LModuleHandle);
3074+
LPLinkMap := LPLinkMap.l_next;
3075+
end;
3076+
Result := 0;
3077+
end;
3078+
{$ENDIF LINUX}
3079+
3080+
{$IFDEF OSX}
3081+
function GetPythonModule: NativeUInt;
3082+
var
3083+
LIndex: integer;
3084+
LName: PAnsiChar;
3085+
LModuleName: string;
3086+
LModuleHandle: NativeUInt;
3087+
begin
3088+
LIndex := 0;
3089+
LName := _dyld_get_image_name(LIndex);
3090+
while (LName <> nil) do begin
3091+
LModuleName := String(LName);
3092+
LModuleHandle := LoadLibrary(PChar(LModuleName));
3093+
if HasSymbols(LModuleHandle) then
3094+
Exit(LModuleHandle);
3095+
Inc(LIndex);
3096+
LName := _dyld_get_image_name(LIndex);
3097+
end;
3098+
Result := 0;
3099+
end;
3100+
{$ENDIF OSX}
3101+
3102+
{$IFDEF MSWINDOWS}
3103+
function GetPythonModule: NativeUInt;
3104+
var
3105+
LHProcess: NativeUInt;
3106+
LHModules: array of NativeUInt;
3107+
LCbNeeded: Cardinal;
3108+
I: Integer;
3109+
LModName: array[0..1024] of char;
3110+
begin
3111+
SetLength(LHModules, 1024);
3112+
LHProcess := OpenProcess(PROCESS_QUERY_INFORMATION + PROCESS_VM_READ, false, GetCurrentProcessId());
3113+
if LHProcess > 0 then begin
3114+
try
3115+
if EnumProcessModules(LHProcess, @LHModules[0], 1024 * SizeOf(HMODULE), LCbNeeded) then begin
3116+
SetLength(LHModules, LCbNeeded div SizeOf(THandle));
3117+
for I := 0 to Length(LHModules) -1 do begin
3118+
GetModuleBaseName(LHProcess, LHModules[I], LModName, SizeOf(LModName));
3119+
if HasSymbols(LHModules[I]) then begin
3120+
Exit(LHModules[I]);
3121+
end;
3122+
end;
3123+
end;
3124+
finally
3125+
CloseHandle(LHProcess);
3126+
end;
3127+
end;
3128+
Result := 0;
3129+
end;
3130+
{$ENDIF MSWINDOWS}
3131+
{$ENDIF FPC}
3132+
3133+
begin
3134+
{$IF DEFINED(LINUX) OR DEFINED(OSX) OR DEFINED(MSWINDOWS)}
3135+
{$IFNDEF FPC}
3136+
Result := GetPythonModule();
3137+
{$ELSE}
3138+
Result := 0;
3139+
{$ENDIF}
3140+
{$ELSE}
3141+
Result := 0;
3142+
{$IFEND}
3143+
end;
3144+
30223145
procedure TDynamicDll.OpenDll(const aDllName : string);
30233146
var
30243147
s : string;
@@ -3107,15 +3230,42 @@ function TDynamicDll.IsHandleValid : Boolean;
31073230
{$ENDIF}
31083231
end;
31093232

3233+
function TDynamicDll.TryLoadFromCurrentProcess: boolean;
3234+
begin
3235+
FDLLHandle := GetPythonModuleFromProcess();
3236+
if not IsHandleValid() then
3237+
Exit(false);
3238+
3239+
BeforeLoad();
3240+
LoadPythonInfoFromModule();
3241+
AfterLoad();
3242+
Result := true;
3243+
end;
3244+
3245+
function TDynamicDll.TryLoadFromHostSymbols: boolean;
3246+
begin
3247+
//We want to look in for host symbols at first
3248+
FDLLHandle := 0;
3249+
Result := HasHostSymbols();
3250+
if Result then
3251+
LoadFromHostSymbols();
3252+
end;
3253+
31103254
procedure TDynamicDll.LoadFromHostSymbols;
3255+
begin
3256+
BeforeLoad();
3257+
LoadPythonInfoFromModule();
3258+
AfterLoad();
3259+
end;
3260+
3261+
procedure TDynamicDll.LoadPythonInfoFromModule;
31113262
var
31123263
LPy_GetVersion: function: PAnsiChar; cdecl;
31133264
LPy_GetProgramFullPath: function: PAnsiChar; cdecl;
31143265
LVersion: string;
31153266
LInfo: TPythonVersionProp;
31163267
LFound: boolean;
31173268
begin
3118-
BeforeLoad();
31193269
//According to the doc:
31203270
//Return the full program name of the Python executable.
31213271
//The value is available to Python code as sys.executable.
@@ -3143,9 +3293,7 @@ procedure TDynamicDll.LoadFromHostSymbols;
31433293
end;
31443294

31453295
if not LFound then
3146-
raise EDLLLoadError.Create('Undetermined Python version from host symbols.');
3147-
3148-
AfterLoad();
3296+
raise EDLLLoadError.Create('Undetermined Python version from loaded module.');
31493297
end;
31503298

31513299
procedure TDynamicDll.LoadDll;
@@ -3155,14 +3303,18 @@ procedure TDynamicDll.LoadDll;
31553303

31563304
procedure TDynamicDll.LoadDllInExtensionModule;
31573305
begin
3158-
//We want to look in for host symbols at first
3159-
FDLLHandle := 0;
3306+
if not ModuleIsLib then
3307+
Exit;
31603308

31613309
FInExtensionModule := True;
3162-
if HasHostSymbols() then
3163-
LoadFromHostSymbols()
3164-
else
3165-
LoadDLL;
3310+
3311+
if TryLoadFromHostSymbols() then
3312+
Exit;
3313+
3314+
if TryLoadFromCurrentProcess() then
3315+
Exit;
3316+
3317+
LoadDLL();
31663318
end;
31673319

31683320
procedure TDynamicDll.UnloadDll;
@@ -3201,9 +3353,6 @@ function TDynamicDll.HasHostSymbols: boolean;
32013353
var
32023354
LPy_IsInitialized: function: integer; cdecl;
32033355
begin
3204-
if not ModuleIsLib then
3205-
Exit(false);
3206-
32073356
LPy_IsInitialized := Import('Py_IsInitialized', false);
32083357
Result := Assigned(LPy_IsInitialized) and (LPy_IsInitialized() <> 0);
32093358
end;
@@ -9241,6 +9390,7 @@ function IsPythonVersionRegistered(PythonVersion : string;
92419390
except
92429391
end;
92439392
end;
9393+
92449394
{$ENDIF}
92459395

92469396
procedure PythonVersionFromDLLName(LibName: string; out MajorVersion, MinorVersion: integer);

0 commit comments

Comments
 (0)