Blog gunsmoker (fordítások) még mindig oka annak, hogy miért ne tegyen semmi rosszat dllmain véletlenszerűen

További okok miatt, hogy ne tegyen semmi rosszat a DllMain-el: véletlenszerű lezárás

A DllMain funkció a boot betöltő zár alatt működik - ez egyike annak a néhány pillanatnak, amikor az operációs rendszer lehetővé teszi a kód végrehajtását a belső zár megtartásakor. Ez azt jelenti, hogy a kódnak nagyon óvatosnak kell lennie, nehogy megtörje a zárolási hierarchiát a DllMainben; máskülönben fenyegetett a holtpont.

(Van egy ilyen hierarchiája, ugye?).

Lezárása a bootloader szükséges minden funkciót el szeretne érni, hogy a lista betöltött DLL a folyamatban. Ez olyan funkciókat is tartalmaz, mint a GetModuleHandle és a GetModuleFileName. Ha a DllMain belép a kritikus szakaszban, vagy vár a szinkronizációs objektumot, és a kritikus szakaszba, vagy egy szinkronizációs objektum tulajdonosa (saját) egy másik szál, ami viszont, várva felszabadulás zárolja a rendszerindítást, az imént létrehozott egy halott zár (holtpont ) most képzeld el, hogy néhány szál boldogan elvégzi az első blokk-kód és belép csGlobal, akkor ellenőrzik peredaytsya valaki mást. Ekkor egy másik szál befejezi munkáját. Ebben az esetben a rakodó zár veszünk, és üzenetet küldött DLL_THREAD_DETACH (a betöltő zár tartott ebben az időben).

Kapsz DLL_THREAD_DETACH és megpróbálsz bejutni az csGlobalba. Ezt az első szálat blokkolja, amely jelenleg a kritikus szakasz tulajdonosa. Ezután a szál folytatja a végrehajtást és felhívja a GetModuleFileName nevet. Ez a funkció megköveteli, hogy a bootloader záródjon (mivel hozzáférést kell biztosítani a folyamatban betöltött DLL-ek listájához), ezért a szál blokkolva van, mert valaki más blokkolja a boot-betöltőt.

Most van holtpont:
  • Az csGlobal tulajdonosa az első téma, amely a bootloader zárására vár.
  • A második szál, amely csGlobalra vár, a boot betöltő zár.
Láttam, hogy történt. És ebben semmi szép.

A történet erkölcse: ne felejtsd el a bootloader zárat. Tegye be azt a kizárási hierarchiában, ha bármilyen zárat szeretne használni a DllMain-ban.

Úgy tűnik, nincs jó elméletem.
Alexander, jól értettem, hogy a névtelen blokk kezdődik / vége, ami általában a könyvtárkódban történik - ez figuratív módon a DllMain eljárás? Mi általában különbség a DllMain és a DllProc között, és milyen sorrendben hajtják végre?

A .dpr DLL fájl kezdő / befejező blokkja meglehetősen bonyolult fordító mágia. Ez a "DllMain" rész, de csak a DLL_PROCESS_ATTACH számára.

A DllEntryPoint a Windows API koncepciója. Valójában ez a modul belépési pontja (IMAGE_OPTIONAL_HEADER.AddressOfEntryPoint). Az exe esetében ez az első kijelentés mutatója, amely végrehajtásra kerül. A DllEntryPoint nem exportált, és nem rendelkezik szimbolikus névvel. A "DllEntryPoint" csak egy betűkészlet, amely egy koncepciót jelez. DLL esetén ez egy olyan mutató, amely "DllMain" -ként fog működni. A magas szintű programozási nyelveken a nyelvi könyvtár (RTL) kódot mindig belépési pontként használják (beleértve a DllMain esetében is). Különösen a Delphi programokban lesz _InitExe és DLL - _InitLib. C ++ esetén ez lesz _DllMainCRTStartup (a DLL-hez).

A DllMain az RTL C ++ koncepciója. Ez egy olyan funkció, amelyet a DllEntryPoint (_DllMainCRTStartup) -tól hívnak, de még nem íródott - a programozó felelőssége megírni. A funkciónak azonosnak kell lennie, különben a linker nem találja meg.

A DllProc az RTL Delphi koncepciója. Ez egy normál funkciómutató (visszahívás, esemény). Ez a DllEntryPoint (_InitLib) nevű. A mutató maga DllProc, de a függvény, amelyre pontokat lehet nevezni, mint bármi - DllProc, DllMain, DllEntryPoint, MySuperDuperHandler. Alapértelmezés szerint (és az esetek 99% -ában) a DllProc nincs kitöltve és = null.

Mi a zavaró itt, a DllMain nem létezik a Delphi-ban, ez az RTL C ++ koncepciója. A DllMain leírása az MSDN-ben található, mert az MSDN a C ++ -ról beszél. Valójában minden "szokáson kívül" másolja ezt a nevet, bár a Delphi kontextusában helyesebb csak DllEntryPointról és DllProcról beszélni.

Mindenesetre a DLL Delphi be- és kirakodásakor a hívások lánca így megy:
- LoadLibrary -> Kernel32 / Ntdll (valahol ott jön markolat boot kritikus szakasz) -> _InitLib (aka ek DIIEntryPoint) -> InitializeModule (csak DLL_PROCESS_ATTACH) -> _StartLib -> DllProc (ha van ilyen) -> InitUnits -> szakasz inicializálási modul -> vissza a kezdet / vég .dpr -> kilépés _InitLib.
- FreeLibrary -> Kerenel32 / Ntdll (valahol ott jön markolat boot kritikus szakasz) -> _InitLib (aka ek DIIEntryPoint) -> _StartLib -> DllProc (ha van ilyen) -> _Halt0 -> FinalizeUnits -> tesztelése szakaszok véglegesítése modulok -> kimenet _InitLib-ből.