Plug-ins
You can attach your own DLL-modules (plug-ins) to the debugger.
This could be useful in two cases:
When you want to extend functionality
of the debugger to meet your specific needs.
When you have a compiler that generates executable modules in
one of those formats that are not supported by the current
format loaders of the debugger. So you may decide to read
appropriate documentation and write it by yourself.
Attachment
Plug-ins are attached to the debugger through four exported functions:
PLUGIN_Init necessarily
| It is called when the module is just loaded.
Besides, presence of this function serves as a mark by which debugger
distinguishes a general DLL module from its own plug-ins.
|
|---|
| PLUGIN_Shutdown | It is called just before
the module is unloaded from the process memory of debugger.
In this function you must free any resources that
were allocated previously.
|
|---|
| PLUGIN_Prop | Called from the plug-ins control dialog
in the debugger. Could be used to change some internal options of the plug-in.
All these options we recommend you to save into the registry.
|
|---|
| PLUGIN_LoadDebugInfo | Used to load symbolic debug information
from each module being loaded into debugged process. The function must return
TRUE, if the symbols have been successfully loaded.
|
|---|
In your plug-ins you can call any service functions
that are declared in file zdapi.h. Copy of this file you can find
in the plug-in control dialog box of the debugger.
More details about these service functions read
here.
Order of plug-ins loading
All plug-ins must reside in one directory,
that is sepcified in plug-in control dialog
opened by pressing ALT+F5 key.
The following algorithm is used to load plug-ins after the debugger
is loaded itself:
the debugger opens plug-ins directory and sequentially
loads each*.dll module found there. If such a DLL exports PLUGIN_Init function,
then it assumed to be a plug-in for the debugger and so left in memory.
Otherwise it is unloaded and no function from it called at all.
There is one case when plug-ins can be reloaded - it happens when
the path of plug-ins is changed. In this case all plug-ins are unloaded,
and then loaded again from the new path.
Using plug-ins
An example of working plug-in module with its source file in C
you can download here.
But here we'll describe shortly only the basic principle.
It is very simple: the first function that is called in your module is
PLUGIN_Init (), which can be used to initialize your global variables.
There you also can setup plug-in's title that debugger uses as the comment
text in plug-in control dialog box. This string can include a short
description of formats supported by the plug-in or just module's
general purpose.
Farther, when debugger detects a new DLL module in debugged process,
it temporary maps this module into its own memory space and gives a chance to
each plug-in to parse file's contents and extract debugging information.
Debugger calls the function PLUGIN_LoadDebugInfofor for each plug-in
until one of them returns TRUE that signals that symbols have been loaded normally.
If all of such functions return FALSE, then the debugging format
is assumed to be unknown and becomes inaccessible.
The function that is called by debugger to load symbols
is declared as following:
BOOL PLUGIN_LoadDebugInfo(BYTE*FileRoot, BYTE*DebugRoot);
When called, parameter #FileRoot points to the memory where
the module's file is mapped.
Pay special attention, that this pointer doesn't point
to the address in the debuggee's process, but instead to the memory where
a copy of the executable module is mapped. Being an executable part of the program,
the module itself always resides in the context of debugged program,
and its sections usually shifted upper relative to their positions in file.
By these two reasons it is more convenient to extract symbols
from a copy of the module mapped into debugger's memory space,
and not from module itself.
Parameter #DebugRoot points to the address where expected to be
a debug section. This parameter is passed only for convenience,
and can be easily calculated from the first parameter and knowing
standard format of executable header which comes here.
Executable file format
If you want to write a symbol loader for some unknown debug information,
then, possibly, you will find useful the following short description
of the executable format itself. This can be in rare cases when by some reasons
debug information is kept in unusual place of an executable file.
In most other cases you can safely use #DebugRoot that points
to the debug section.
In the following table we describe the structure of the standard
executable *.exe or *.dll file. All potentially useful fields
are in bold font. Other detailed information can be taken from MSDN,
and all structures declared in winnt.h.
| Data type | Name | Useful content
|
|---|
| IMAGE_DOS_HEADER | DosHeader | Points to NT header
|
|---|
| a space of variable length...
| | DWORD | Signature | 'PE'
|
|---|
| IMAGE_FILE_HEADER | FileHeader | Number of sections and module characteristics
|
|---|
| IMAGE_OPTIONAL_HEADER32 | OptionalHeader | Entry point, image base, sizes, version and others
|
|---|
| IMAGE_DATA_DIRECTORY | Export | Exported symbols
|
|---|
| IMAGE_DATA_DIRECTORY | Import | Imported symbols
|
|---|
| IMAGE_DATA_DIRECTORY | Resource | Dialogs, images and so on
|
|---|
| IMAGE_DATA_DIRECTORY | Exception | Exception Directory
|
|---|
| IMAGE_DATA_DIRECTORY | Security | Security Directory
|
|---|
| IMAGE_DATA_DIRECTORY | BaseReloc | Base Relocation Table
|
|---|
| IMAGE_DATA_DIRECTORY | Debug | Pointer to debug symbols
|
|---|
| IMAGE_DATA_DIRECTORY | Copyright/Architecture | Architecture Specific Data
|
|---|
| IMAGE_DATA_DIRECTORY | GlobalPtr | RVA of GP
|
|---|
| IMAGE_DATA_DIRECTORY | TLS | TLS Directory
|
|---|
| IMAGE_DATA_DIRECTORY | LoadConfig | Load Configuration Directory
|
|---|
| IMAGE_DATA_DIRECTORY | BoundImport | Bound Import Directory in headers
|
|---|
| IMAGE_DATA_DIRECTORY | IAT | Import Address Table
|
|---|
| IMAGE_DATA_DIRECTORY | DelayImport | Delay Load Import Descriptors
|
|---|
| IMAGE_DATA_DIRECTORY | COM_Descriptor | COM Runtime descriptor
|
|---|
| a space of variable length...
| | IMAGE_SECTION_HEADER | Sections []
| Sections of different data, including section of code (usually one).
Each section has the name of no more than eight characters length
|
|---|
Note: all sections of executable module usually shifted down to the beginning of the file. This is done by removing page-aligned spaces between them. But when the module is loaded by the system, these sections are aligned on page boundaries.
By this reason some members in the header have VA (virtual address) prefix, meaning that the value is referencing the address in the process's (virtual) memory and not in file's (physical) memory. To calculate the physical address for such a value we need to subtract #delta from it:
delta = section->VirtualAddress - section->PointerToRawData
Where #section - is the section where the VirtualAddress resides. To find this section write a special function FindSectionByVA, that scans all sections comparing their ranges with desired address.
Все исходные файлы и рабочий DLL-модуль вы можете скачать из этого
архива.
Для того, чтобы его увидел и загрузил отладчик, файл sample.dll следует
переместить в текущую директорию плагинов.
All your plug-in modules must be placed into plug-ins
directory, otherwise debugger will not find them.
|