There are simple and even not so simple circumstances in which the LoadLibrary call from DllMain is completely safe. But the design is that DllMain hopes not to change the list of loaded modules.
Although storing the loader lock does indeed limit what can be done in DllMain, this indirectly applies only to the LoadLibrary rule. The corresponding purpose of blocking the bootloader is serialized access to the list of loaded modules. Although NTDLL runs on this list in one thread, owning a bootloader lock ensures that the list is not changed by NTDLL code that runs in another thread. However, locking the bootloader is a critical section. It does nothing to stop the same thread from re-acquiring the bootloader lock and changing the list.
It would not matter if NTDLL completely retained itself while working on the list. However, NTDLL provides for the use of other code in this work, as in the initialization of a recently loaded DLL. Each time NTDLL gets called out while working on a list, there is a choice for design. In general, there are two options. One of them is to stabilize the list and release the bootloader lock, call outside, then get the bootloader lock and resume work on the list, as if from scratch, because an external call could change it. Another is to lock the bootloader and trust the called code not to do anything that modifies the list. Thus, LoadLibrary becomes unavailable in DllMain.
Not that the bootloader lock did anything to stop DllMain from calling LoadLibrary, or even that the bootloader lock itself makes such a call unsafe. Instead, while retaining the loader lock, NTDLL trusts DllMain not to call LoadLibrary.
For comparison, consider the DllMain rule that you do not expect synchronization objects. Here, locking the bootloader plays a direct role in making it unsafe. Waiting for a synchronization object in DllMain establishes the ability to lock. All that is needed is that another thread already contains the object that you expect, and then this other thread calls any function that will wait for the loader to lock (for example, LoadLibrary, as well as functions such as the seemingly harmless GetModuleHandle )
Desiring to stretch or break DllMain rules can be mischievous or even stupid. However, I must point out that Microsoft is at least partly to blame for asking people how strong or significant these rules are. In the end, some of them were not always documented clearly and decisively, and when I last watched, they were still not documented in all situations where they are definitely needed. (The exception I have in mind is that, at least until Visual Studio 2005, MFC programmers writing DLLs were asked to put their initialization code in CWinApp :: InitInstance, but they were not told that this code obeys DllMain rules.)
In addition, it would be a little rich for any Microsoft to speak as if the DllMain rules should be followed without any questions. There are examples where Microsoft programmers break the rules and continue even after breaking the rules, which caused serious problems with the real world.