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
ULONG PackagedBinary : 1;
6.2 and higher
ULONG MarkedForRemoval : 1;
6.2 and higher
ULONG ImageDll : 1;
6.2 and higher
ULONG LoadNotificationsSent : 1;
6.2 and higher
ULONG TelemetryEntryProcessed : 1;
6.2 and higher
ULONG ProcessStaticImport : 1;
6.2 and higher
ULONG InLegacyLists : 1;
6.2 and higher
ULONG InIndexes : 1;
6.2 and higher
ULONG ShimDll : 1;
6.2 and higher
ULONG InExceptionTable : 1;
6.2 and higher
ULONG ReservedFlags1 : 2;
6.2 and higher
ULONG LoadInProgress : 1;
6.2 and higher
ULONG ReservedFlags2 : 1;
6.2 to 6.3
ULONG LoadConfigProcessed : 1;
10.0 and higher
ULONG EntryProcessed : 1;
6.2 and higher
ULONG ProtectDelayLoad : 1;
10.0 and higher
ULONG ReservedFlags3 : 3;
6.2 to 6.3
ULONG ReservedFlags3 : 2;
10.0 and higher
ULONG DontCallForThreads : 1;
6.2 and higher
ULONG ProcessAttachCalled : 1;
6.2 and higher
ULONG ProcessAttachFailed : 1;
6.2 and higher
ULONG CorDeferredValidate : 1;
6.2 and higher
ULONG CorImage : 1;
6.2 and higher
ULONG DontRelocate : 1;
6.2 and higher
ULONG CorILOnly : 1;
6.2 and higher
ULONG ChpeImage : 1;
1803 and higher
ULONG ReservedFlags5 : 3;
6.2 to 1709
ULONG ReservedFlags5 : 2;
1803 and higher
ULONG Redirected : 1;
6.2 and higher
ULONG ReservedFlags6 : 2;
6.2 and higher
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: