How to get address of Delphi code label in another function?
I am trying to migrate some code from Delphi 5 to Delphi XE7-WIN64. The scenario is latest Delphi doesn't allow mixed assembly and Delphi code. Also I am asm newbie.
Original Code:
function TclDbgHelpStackTracer.GetSymbolSearchPath(): string;
var
sPath: array[0..MAX_PATH] of char;
mbi: MEMORY_BASIC_INFORMATION;
pProc: Pointer;
label l1;
begin
asm
mov eax, offset l1
mov pProc, eax
end;
l1:
Result := '';
if (GetEnvironmentVariable(SYMBOL_PATH) <> '') then
Result := GetEnvironmentVariable(SYMBOL_PATH) + ';';
if (GetEnvironmentVariable(ALTERNATE_SYMBOL_PATH) <> '') then
Result := Result + GetEnvironmentVariable(ALTERNATE_SYMBOL_PATH) + ';';
if (GetEnvironmentVariable('SystemRoot') <> '') then
Result := Result + GetEnvironmentVariable('SystemRoot') + ';';
VirtualQuery(pProc, mbi, sizeof(mbi));
GetModuleFileName(Cardinal(mbi.AllocationBase), sPath, MAX_PATH);
StrRScan(sPath, '')^ := #0;
Result := Result + sPath + ';';
GetModuleFileName(0, sPath, MAX_PATH);
StrRScan(sPath, '')^ := #0;
Result := Result + sPath;
end;
The above code works in Delphi XE7-WIN32 But as you can see the above snippet has nested asm block Delphi XE7-WIN64 requires procedures/functions to only contain asm or pascal code. So I changed it to following:
{$IFDEF WIN64}
procedure AsmProc(pProc: Pointer);
asm
mov eax, offset l1
mov pProc, eax
end;
{$ENDIF}
function TclDbgHelpStackTracer.GetSymbolSearchPath(): string;
var
sPath: array[0..MAX_PATH] of char;
mbi: MEMORY_BASIC_INFORMATION;
pProc: Pointer;
label l1;
begin
{$IFDEF WIN32}
asm
mov eax, offset l1
mov pProc, eax
end;
{$ELSE}
AsmProc(pProc);
{$ENDIF}
l1:
Result := '';
if (GetEnvironmentVariable(SYMBOL_PATH) <> '') then
Result := GetEnvironmentVariable(SYMBOL_PATH) + ';';
if (GetEnvironmentVariable(ALTERNATE_SYMBOL_PATH) <> '') then
Result := Result + GetEnvironmentVariable(ALTERNATE_SYMBOL_PATH) + ';';
if (GetEnvironmentVariable('SystemRoot') <> '') then
Result := Result + GetEnvironmentVariable('SystemRoot') + ';';
VirtualQuery(pProc, mbi, sizeof(mbi));
GetModuleFileName(Cardinal(mbi.AllocationBase), sPath, MAX_PATH);
StrRScan(sPath, '')^ := #0;
Result := Result + sPath + ';';
GetModuleFileName(0, sPath, MAX_PATH);
StrRScan(sPath, '')^ := #0;
Result := Result + sPath;
end;
Now here the trouble starts. l1 is code label, its address (address of next executable statement) is moved to eax then pointer pProc is made to point along this address. and then pointer pProc is used in
VirtualQuery(pProc, mbi, sizeof(mbi));
Question Is How to pass address of l1? Or is there any other method to do the same?
The code in the question makes a real meal of extracting the name of the module that contains the executing code. All you need is this:
function TclDbgHelpStackTracer.GetSymbolSearchPath(): string;
function GetEnvPath(const Name: string): string;
var
Value: string;
begin
Value := GetEnvironmentVariable(Name);
if Value <> '' then
Result := Value + ';'
else
Result := '';
end;
function GetModulePath(Module: HMODULE): string;
begin
Result := ExtractFileDir(GetModuleName(Module));
end;
begin
Result :=
GetEnvPath(SYMBOL_PATH) +
GetEnvPath(ALTERNATE_SYMBOL_PATH) +
GetEnvPath('SystemRoot') +
GetModulePath(HInstance) + ';' +
GetModulePath(0);
end;
The asm
code in your question was, frankly, bizarre. There's just no need to do it that way. All the code was attempting to do was find the module handle which is already available as the global HInstance
variable.
As a general rule, you should try to avoid using asm
. It makes code less portable and harder to understand. There are times when using asm
is the right choice. This is not one of those occasions.