Question

Is there a way to access the Jedi Debug Information (JDBG) contained in an executable?

Microsoft debugging tools have pointed me to a stack chain in my binary, and i want to know what methods/procedures/functions these offsets correspond to:

user32.dll!SendMessageA+0x4c
StackOverflow.exe+0x179263
StackOverflow.exe+0x2315b5
StackOverflow.exe+0x1fc82
StackOverflow.exe+0x50388
StackOverflow.exe+0x541fe
user32.dll!gapfnScSendMessage+0x332

Obviously i'm calling SendMessage, but i don't know from where. The executable was built with Jcl Debug info embedded in the executable; but i can't figure out how to read it.

Looking at some of the functions and classes in JclDebug.pas, everything seems to be geared around getting debug information for inside the current process, e.g.:

function GetLocationInfo(const Addr: Pointer; var Info: TJclLocationInfo): Boolean;

takes an address in my current process's address space. It the figures out which HMODULE the address is in, e.g.:

  • Stackoverflow.exe
  • GDI32.dll
  • USER32.dll
  • KERNELBASE.dll
  • dwmapi.dll
  • UxTheme.dll

i was thinking i could use LoadLibrary (which returns an HMODULE) to manually load a module, then feed it to some of the classes that dig through module images for debug info:

module := LoadLibrary('C:\Users\Ian\Desktop\StackOverflow.exe');

and

TJclDebugInfoList = class(TObjectList)
private
   function GetItemFromModule(const Module: HMODULE): TJclDebugInfoSource;
   ...
protected
   function CreateDebugInfo(const Module: HMODULE): TJclDebugInfoSource;
   ...
end;

except it's protected.

i'm trying (hoping) i can write a tool where i pick the binary (*.exe), enter an address, and get returned the

  • function
  • method
  • file
  • line number

of the offset.

e.g.

[002315B5] FMain.TfrmMain.lvQuestions (Line 158, "FMain.pas" + 1) + $11

Possible?


Edit: My first, rough and ready approach, was to just extract the compressed map file so i could look at it. But it's not saved as a resource (?):

enter image description here

Although a general tool would be more useful:

enter image description here


Update:

i tried using TJclDebugInfoList; i realized that the array property ItemFromModule would access the protected method:

function GetModuleLocationInfo(filename: string; Addr: Pointer): TJclLocationInfo;
var
   module: HMODULE;
   infoList: TJclDebugInfoList;
   infoSource: TJclDebugInfoSource;

   Address: Pointer;
   locationInfo: TJclLocationInfo;
   AddressOffset: Integer;
begin
   module := LoadLibrary(filename);
   if module = 0 then
      RaiseLastWin32Error;
   try
      infoList := TJclDebugInfoList.Create;
      try
         infoSource := infoList.ItemFromModule[module];
         if source = nil then
            raise Exception.Create('Could not find debug info source for module '+IntToStr(module));
         if not source.GetLocationInfo(Addr, {var}locationInfo) then
            raise Exception.Create('Could not get location info for address $'+IntToHex(Integer(Address), 8));

         Result := locationInfo;
      finally
         infoList.Free;
      end;
   finally
      FreeLibrary(module);
   end;
end;

Except that the code in one of the TJclDebugInfoSource descendant classes gets an underflow when it tries to convert what it assumes is a virtual address to an offset address.

Was it helpful?

Solution

Create a TJclDebugInfoBinary object, using the HModule handle you get from LoadLibrary. Then call GetLocationInfo on it. That's all TJclDebugInfoList would have done anyway, except it has some helper methods to map addresses from the current address space to the modules they correspond to, whereas when you do it manually, you'll have to already know which module the addresses belong to. (But the crash dump already told you that part, so you don't need the help of the list class.)

You'll probably have to massage the addresses because the base address of the module at the time of the crash is not going to be the same as when you've loaded it with LoadLibrary.

The JCL debug information is not stored in a resource. It's stored in a PE section named JCLDEBUG. See the uses of PeMapImgFindSection32 and PeMapImgFindSectionFromModule in JclDebug.pas.

OTHER TIPS

I made such a tool a while ago, don't know if I can find it back again, but at least it is possible :-)

On the other hand, I made several tools using jclDebug.pas, and now I remember: I made some changes to it to make "offline" stack tracing possible. You can take a look at these:

Live Process Stack Viewer: http://code.google.com/p/asmprofiler/wiki/ProcessStackViewer

Minidump reader (using offline reading .map or embedded jdbg info from exe): http://code.google.com/p/asmprofiler/source/browse/#svn%2Ftrunk%2FMiniDumpReader

Here's the code can give debug information about an address in a module

function GetModuleLocationInfo(filename: string; AddressOffset: Pointer; AssumeOffsetIsRelativeToStartOfCodeSection: Boolean=False): TJclLocationInfo;
var
    module: HMODULE;
    infoList: TJclDebugInfoList;
    infoSource: TJclDebugInfoSource;

    Address: Pointer;
    locationInfo: TJclLocationInfo;
begin
    //Code is public domain. No attribution required.
    module := LoadLibrary(PChar(filename));
    if module = 0 then
        RaiseLastWin32Error;
    try
        infoList := TJclDebugInfoList.Create;
        try
            infoSource := infoList.ItemFromModule[module];
            if infoSource = nil then
                raise Exception.Create('Could not find debug info source for module '+IntToStr(module));

            DWORD(Address) := DWORD(AddressOffset) + DWORD(module) + DWORD(ModuleCodeOffset);
            if not infoSource.GetLocationInfo(Address, {var}locationInfo) then
                raise Exception.Create('Could not get location info for address $'+IntToHex(Integer(Address), 8));

            Result := locationInfo;
        finally
            infoList.Free;
        end;
    finally
        FreeLibrary(module);
    end;
end;

And a practical example, an offset from Process Explorer:

enter image description here

GetModuleLocationInfo('C:\Program Files (x86)\Avatar\HelpDesk.exe', 0xdcb17);

returns:

TJclLocationInfo
   Address: $266CB17
   UnitName: 'BalloonHint'
   ProcedureName: 'TBalloonHint.SetVisible'
   OffsetFromProcName: 83
   LineNumber: 281
   OffsetFromLineNumber: 0
   SourceName: 'BalloonHint.pas'
   DebugInfo: $1F25C74

Or in the style of JclDebug:

[0266CB17] BalloonHint.TBalloonHint.SetVisible (Line 281, "BalloonHint.pas") + $0

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top