Reputation: 626
I am trying to look up the unit name and function name in the "Detailed" map file that is produced by building a project in Delphi 5. I found some code online that claims to do this but I can't make it work.
Code Requirements:
Goal: The function: 'Log' is supposed to return the address of the calling procedure. Once the address has been determined the unit and function names as well as line number can be looked up in the map file.
Purpose: Wouldn't it be nice... if the name of a function could be obtained just by calling 'Log' from anywhere in a program.
Reality: I am really interested in learning what is going on in the 'Log' function, and why or why not it is working, secondary to this would be an alternitave way of returning the unit and function name as well as line number of a calling procedure.
The Problem: The address I get from TForm1.Button1Click > TForm1.Log > TForm1.LogAddress > TForm1.ShowInfo does not coincide with the corresponding items in the map file I am looking for i.e. M=Unit1, TForm1.Button1Click, etc...
Websites: http://www.haydenr.com/delphi/articles/article002.htm , http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22596248.html The last website may not be accessible from the link -- I put the Google search string in the 'Log' function.
Delphi Code:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
Procedure Log;
Procedure ShowInfo(hexAddress : Integer);
Procedure LogAddress(ptr: pointer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Implementation
{$R *.DFM}
Procedure TForm1.Log;
// Google Search: capturing a procedure or function's name for logging purposes
// http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22596248.html
Begin
ASM
pop EAX
push EAX
Call LogAddress
End;//ASM
End;
Procedure TForm1.ShowInfo(hexAddress: Integer);
// http://www.haydenr.com/delphi/articles/article002.htm
Var
iMapFileAddress : Integer;
sMapFileAddress : String;
ImageBase : Integer;
SubOffset : Integer;
Offset : Integer;
Begin
ImageBase := $00400000; // Project > Options... > Linker Tab > Memory sizes group box > Image Base
SubOffset := $1000;
Offset := ImageBase + SubOffset;
iMapFileAddress := hexAddress - Offset;
sMapFileAddress := IntToHex(iMapFileAddress,8);
Edit1.Text := sMapFileAddress; //This is the value I get: sMapFileAddress = 00542214
{
Here are some excerpts from: Project1.map
|Detailed map of segments
| 0001:00040498 000002D4 C=CODE S=.text G=(none) M=Unit1 ACBP=A9
| 0001:0004076C 000001A5 C=CODE S=.text G=(none) M=Project1 ACBP=A9
| :
| :
| :
| Address Publics by Name
|
| 0001:0004071C TForm1.Button1Click
| 0001:00040668 TForm1.Log
| 0001:00040700 TForm1.LogAddress
| 0001:0004067C TForm1.ShowInfo
}
End;
procedure TForm1.LogAddress(ptr: pointer);
begin
ShowInfo(Integer(ptr));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Log;
end;
end.
Upvotes: 4
Views: 2283
Reputation: 626
After making the code replacements and adjusting the "esp offset" as per Rob Kennedy's suggestions the working code follows...
In order to produce a "Map File"; in the Delphi IDE follow these steps:
Project > Options... > Compiler Tab > Code generation pane: Optimization = FALSE
Project Options Window > Linker Tab > Map file pane: Select "Detailed"
Project > Build All Projects
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1 : TButton;
Label1 : TLabel;
Edit1 : TEdit;
procedure Button1Click(Sender: TObject);
Procedure Log;
Procedure ShowInfo(hexAddress : Integer);
Procedure LogAddress(ptr: pointer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
Implementation
{$R *.DFM}
Procedure TForm1.Log;
// Google Search: capturing a procedure or function's name for logging purposes
// http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22596248.html
Begin
ASM
// fetch return address from top of stack
mov edx,[esp+8]
call LogAddress
End;//ASM
End;
Procedure TForm1.ShowInfo(hexAddress: Integer);
// http://www.haydenr.com/delphi/articles/article002.htm
Var
iMapFileAddress : Integer;
sMapFileAddress : String;
ImageBase : Integer;
SubOffset : Integer;
Offset : Integer;
Begin
ImageBase := $00400000; // Project > Options... > Linker Tab > Memory sizes group box > Image Base
SubOffset := $1000;
Offset := ImageBase + SubOffset;
iMapFileAddress := hexAddress - Offset;
sMapFileAddress := IntToHex(iMapFileAddress,8);
Edit1.Text := sMapFileAddress; //This is the value I get: sMapFileAddress = 00040730
{
|Here are some excerpts from: Project1.map
|*******************************************************************************************************************
|Detailed map of segments
| 0001:00040498 000002D4 C=CODE S=.text G=(none) M=Unit1 ACBP=A9
| 0001:0004076C 000001A5 C=CODE S=.text G=(none) M=Project1 ACBP=A9
| :
| :
| V
| Address Publics by Name
|
| 0001:0004071C TForm1.Button1Click
| 0001:00040668 TForm1.Log
| 0001:00040700 TForm1.LogAddress
| 0001:0004067C TForm1.ShowInfo
| :
| :
| V
|Line numbers for Unit1(Unit1.pas) segment .text
|
| 82 0001:00040728 83 0001:00040730 85 0001:00040764 85 0001:0004076B
|*******************************************************************************************************************
|
| Returned Hex (H) = 00040730
| Returned Integer (I) = 263984
|-------------------------------------------------------------------------------------------------------------------
|Address Of Min Hex Max Hex Min Integer Max Integer | Evaluation
|------------------------------------------------------------------------------|------------------------------------
|Unit1 00040498 000406E5 263320 263909 | Hmin < H < Hmax AND Imin < I < Imax
|Function/Procedure 0004071C 263964 | H < Hmax AND I < Imax
|Line Number 00040730 263984 | H = Hmax AND I = Imax
|-------------------------------------------------------------------------------------------------------------------
|
|NOTE: The actual line number where 'Log' is called is: Returned Line Number - 1 (83 - 1 = 82)
}
End;
procedure TForm1.LogAddress(ptr: pointer);
begin
ShowInfo(Integer(ptr));
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Log;
end;
end.
Upvotes: 1
Reputation: 163247
You're not calling LogAddress
properly. As a method of TForm1
, it's really a two-argument function. The two arguments are Self
and ptr
, in that order, which means that register EAX is Self
and EDX is ptr
. However, you're passing the address parameter in EAX, and you're doing nothing about EDX. That means you're looking up the name of who-knows-what, which probably isn't the address of any code section in your program. On top of that, the Self
value isn't valid anymore, so any references to member variables (such as Edit1
) won't work; you'll get an access violation or some form of memory corruption.
Change the Log
method to this:
procedure TForm1.Log;
asm
// fetch return address from top of stack
mov edx, [esp+4]
call LogAddress
end;
Keep in mind that the address you're passing in the ptr
parameter is the return address of the Log
call. In practice, that's usually the address of the instruction after the CALL
instruction used to get into the Log
method. Therefore, you should not expect to find that exact address in the list of functions in the map file. Instead, you'll need to look for the function whose address is the closest without exceeding the address you have. Furthermore, if you convert that address to a line number, it will likely be the line after the actual call site.
The JclDebug unit, in the JCL, already has functions that will do this for you. You can use the ProcByLevel
function to get the name of the caller and LiveByLevel
to get the line number.
Upvotes: 2