Flags in the LDR_DATA_TABLE_ENTRY

From its origin in Windows NT 3.1 as the definitive record of a loaded module, the LDR_DATA_TABLE_ENTRY structure has a dword of Flags. These might be just an internal detail of the boot loader’s, the kernel’s and NTDLL’s management of loaded modules—something useful to know when debugging but nothing to use safely in programming—except that the Flags are copied whole to the RTL_PROCESS_MODULE_INFORMATION structure. Thus are the Flags exposed even to user-mode software from the beginning of Windows.

In the many years since this beginning, interpreting the Flags has got complicated. The LDR_DATA_TABLE_ENTRY originally served all three of the boot loader, kernel and NTDLL. Each has its own loader of executable modules—at the very least, each loads the next—but although the three loaders are essentially independent, their early versions all use the same LDR_DATA_TABLE_ENTRY as their structure for managing any one loaded module. Within this structure’s Flags, some bits are meaningful to all the loaders, most only to NTDLL, and a few only to the kernel.

Microsoft’s names for these bits as long ago as Windows NT 3.51 are known from the output of the !dlls command as implemented (then) by the KDEXTX86.DLL debugger extension from the contemporaneous Device Driver Kit (DDK). That a command for listing user-mode DLLs shows flags that look to have no meaning to the user-mode loader suggests most obviously two possibilities: I should look harder in my inspection of the binaries; or the writer of the debugger extension, possibly far removed from any programmers of the loaders, simply had !dlls show all the defined flags. Suppose the latter and then disregard bits whose known use in version 3.51 has no corresponding use in version 3.10, and the following is surely a reasonable guess for Microsoft’s original definitions of the Flags bits:

#define LDRP_STATIC_LINK                0x00000002      // ntdll
#define LDRP_IMAGE_DLL                  0x00000004      // ntdll
#define LDRP_LOAD_IN_PROGRESS           0x00001000      // ntdll
#define LDRP_UNLOAD_IN_PROGRESS         0x00002000      // ntdll
#define LDRP_ENTRY_PROCESSED            0x00004000      // ntldr, ntoskrnl and ntdll
#define LDRP_ENTRY_INSERTED             0x00008000
#define LDRP_CURRENT_LOAD               0x00010000
#define LDRP_FAILED_BUILTIN_LOAD        0x00020000      // ntoskrnl

When Windows XP specialised the LDR_DATA_TABLE_ENTRY into a new KLDR_DATA_TABLE_ENTRY for the kernel-mode loader, keeping the LDR_DATA_TABLE_ENTRY for the user-mode loader, the many members that were carried from the old to the new included the Flags. For a few versions more, the two structures look to have shared the one list of Flags bits.

Bit Fields

Starting with version 6.2, what had just been a ULONG for Flags is elaborated formally as bit fields:

Mask Definition Versions
0x00000001
ULONG PackagedBinary : 1;
6.2 and higher
0x00000002
ULONG MarkedForRemoval : 1;
6.2 and higher
0x00000004
ULONG ImageDll : 1;
6.2 and higher
0x00000008
ULONG LoadNotificationsSent : 1;
6.2 and higher
0x00000010
ULONG TelemetryEntryProcessed : 1;
6.2 and higher
0x00000020
ULONG ProcessStaticImport : 1;
6.2 and higher
0x00000040
ULONG InLegacyLists : 1;
6.2 and higher
0x00000080
ULONG InIndexes : 1;
6.2 and higher
0x00000100
ULONG ShimDll : 1;
6.2 and higher
0x00000200
ULONG InExceptionTable : 1;
6.2 and higher
 
ULONG ReservedFlags1 : 2;
6.2 and higher
0x00001000
ULONG LoadInProgress : 1;
6.2 and higher
0x00002000
ULONG ReservedFlags2 : 1;
6.2 to 6.3
ULONG LoadConfigProcessed : 1;
10.0 and higher
0x00004000
ULONG EntryProcessed : 1;
6.2 and higher
0x00008000
ULONG ProtectDelayLoad : 1;
10.0 and higher
 
ULONG ReservedFlags3 : 3;
6.2 to 6.3
ULONG ReservedFlags3 : 2;
10.0 and higher
0x00040000
ULONG DontCallForThreads : 1;
6.2 and higher
0x00080000
ULONG ProcessAttachCalled : 1;
6.2 and higher
0x00100000
ULONG ProcessAttachFailed : 1;
6.2 and higher
0x00200000
ULONG CorDeferredValidate : 1;
6.2 and higher
0x00400000
ULONG CorImage : 1;
6.2 and higher
0x00800000
ULONG DontRelocate : 1;
6.2 and higher
0x01000000
ULONG CorILOnly : 1;
6.2 and higher
0x02000000
ULONG ChpeImage : 1;
1803 and higher
 
ULONG ReservedFlags5 : 3;
6.2 to 1709
ULONG ReservedFlags5 : 2;
1803 and higher
0x10000000
ULONG Redirected : 1;
6.2 and higher
 
ULONG ReservedFlags6 : 2;
6.2 and higher
0x80000000
ULONG CompatDatabaseProcessed : 1;
6.2 and higher

Though some programmer at Microsoft evidently went to some trouble to organise the Flags into bit fields for Windows 8, and may even have intended to define all the bits that are meaningful to the user-mode loader, the result is not complete and current. Certainly, the two bits that are named ReservedFlags1 both have new use even for Windows 8. The 0x00000400 bit is set in the entry for the Application Verifier provider. The 0x00000800 bit records that the shim engine (apphelp.dll) has been sent its initial notification (SE_DllLoaded) about this entry.

Macro Definitions

If earlier versions defined the Flags bits symbolically, it will have been through macros. These would not pass even into private symbols, but Microsoft’s names for some of the bits are known with good confidence from the output of the !dlls command as implemented in debugger extensions KDEXTX86.DLL at first and then EXTS.DLL in Windows XP and higher:

RESEARCH AND WRITING IN PROGRESS