├── .gitignore ├── CODE-LICENSE-GPL2 ├── CODE-LICENSE-GPL3 ├── CODE-LICENSE-MIT ├── LICENSE ├── README.md ├── analysis-commands.md ├── code ├── README.md ├── glibc │ ├── README.md │ ├── atexit │ │ ├── Makefile │ │ ├── README.md │ │ ├── gdb-log.html │ │ ├── lib1.c │ │ ├── lib2.c │ │ └── main.c │ ├── data-symbol │ │ ├── Makefile │ │ ├── README.md │ │ ├── lib1.c │ │ └── main.c │ ├── dlopen-init-interruption │ │ ├── Makefile │ │ ├── README.md │ │ ├── lib1.c │ │ ├── lib2.c │ │ └── main.c │ ├── dlopen-noload │ │ ├── initialization │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── lib1.c │ │ │ ├── lib2.c │ │ │ ├── lib3.c │ │ │ └── main.c │ │ └── reference-count │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── lib1.c │ │ │ └── main.c │ ├── dlopen-reentrancy │ │ ├── Makefile │ │ ├── README.md │ │ ├── gdb-cmds.txt │ │ ├── gdb-log.html │ │ ├── lib1.c │ │ ├── lib2.c │ │ └── main.c │ ├── dlopen-thread-join-dlopen │ │ ├── Makefile │ │ ├── README.md │ │ ├── gdb-log.txt │ │ ├── lib1.c │ │ ├── lib2.c │ │ └── main.c │ ├── dlopen-thread-join │ │ ├── Makefile │ │ ├── README.md │ │ ├── lib1.c │ │ ├── lib2.c │ │ └── main.c │ ├── dlopen │ │ ├── Makefile │ │ ├── README.md │ │ ├── gdb-log.html │ │ ├── lib1.c │ │ ├── lib2.c │ │ └── main.c │ ├── lazy-bind │ │ ├── dynamic-link │ │ │ ├── Makefile │ │ │ ├── lib1.c │ │ │ ├── lib2.c │ │ │ └── main.c │ │ └── dynamic-load │ │ │ ├── Makefile │ │ │ ├── lib1.c │ │ │ ├── lib2.c │ │ │ └── main.c │ └── thread-performance │ │ ├── Makefile │ │ └── thread-test.c └── windows │ ├── README.md │ ├── data-export │ ├── README.md │ ├── build.bat │ ├── dll-test.c │ └── exe-test.c │ ├── dll-init-order-test │ ├── build.bat │ ├── dll-test.cpp │ └── exe-test.c │ ├── dll-process-detach-test-harness │ ├── build.bat │ └── dll-process-detach-test-harness.c │ ├── dll-worker-thread-return-value-rust │ ├── executable │ │ ├── Cargo.toml │ │ └── src │ │ │ └── main.rs │ └── library │ │ ├── Cargo.toml │ │ └── src │ │ └── lib.rs │ ├── dll-worker-thread-return-value │ ├── build.bat │ ├── exe-test.c │ └── lib.cpp │ ├── event-experiment │ ├── build.bat │ └── event-experiment.c │ ├── fls-experiment │ ├── build.bat │ └── fls-experiment.c │ ├── library-init-lazy-load │ ├── README.md │ ├── build.bat │ ├── lib1.c │ └── main.c │ ├── library-init-thread-join │ ├── README.md │ ├── build.bat │ ├── dll-test.c │ └── exe-test.c │ ├── loadlibrary-concurrent-work │ ├── README.md │ ├── build.bat │ ├── dll-test.c │ └── exe-test.c │ ├── loadlibrary-init-circular-dependency │ ├── README.md │ ├── build.bat │ ├── exe-test.c │ ├── lib1.c │ └── lib2.c │ ├── loadlibrary │ ├── build.bat │ ├── dll-test.c │ └── exe-test.c │ ├── ntterminateprocess-test-harness │ ├── build.bat │ └── ntterminateprocess-test-harness.c │ └── thread-performance │ ├── native-thread-fls-alloc │ ├── build.bat │ ├── dll-test.c │ └── thread-test.c │ ├── native-thread-no-loader-init │ ├── build.bat │ └── thread-test.c │ └── native-thread │ ├── build.bat │ └── thread-test.c └── data └── windows ├── LdrShutdownProcess-trace.log ├── LdrpDrainWorkQueue-decompiled.txt ├── RtlpWaitOnCriticalSection-NtTerminateProcess-call-stack-sample.txt ├── com-components ├── com-components.txt └── get-com-components.ps1 ├── dll-best-practices ├── DLL_bestprac.doc └── README.md ├── dll-deps-research ├── delay-loads.txt ├── dlls-depending-on-service-dlls.txt ├── dumpbin-delay-loads.ps1 ├── dumpbin-dir.ps1 ├── dumpbin-find-dlls-depending-on-dlls.ps1 ├── get-service-dlls.ps1 ├── process-count.ps1 └── service-dlls.txt ├── load-all-modules-ldr-ddag-node-state-trace.txt ├── loadlibrary-trace.log ├── ms-devblogs-search ├── README.md ├── dllmain.txt └── loader-lock.txt ├── ms-docs-search ├── README.md ├── cpp-docs-dllmain.txt ├── cpp-docs-loader-lock.txt ├── sdk-api-dllmain.txt ├── sdk-api-loader-lock.txt ├── win32-dllmain.txt └── win32-loader-lock.txt ├── timeline-verification ├── README.md └── dll-imports-exports │ ├── windows-31 │ ├── KRNL286-exports.txt │ └── KRNL386-exports.txt │ └── windows-nt-31 │ ├── KERNEL32-exports.txt │ ├── KERNEL32-imports.txt │ ├── NTDLL-exports.txt │ └── NTDLL-imports.txt └── winhttp-dllmain-debugging.log /.gitignore: -------------------------------------------------------------------------------- 1 | main 2 | target 3 | *.out 4 | *.so 5 | *.exe 6 | *.dll 7 | *.obj 8 | *.ilk 9 | *.lib 10 | *.exp 11 | *.pdb 12 | *.lock 13 | *.sw* 14 | -------------------------------------------------------------------------------- /CODE-LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) 2023-2025 Elliot Killick 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /code/README.md: -------------------------------------------------------------------------------- 1 | # Code Samples 2 | 3 | ## Compiling & Running 4 | 5 | These instructions are for building and running the code samples in this repo: 6 | 7 | 1. Compile 8 | - Unix: Run `make` where there is a `Makefile` 9 | - Windows: Run `build.bat` where this script exists 10 | - For cross-platform Rust: `cargo build` 11 | 2. Pre-execution 12 | - Unix: Run `export LD_LIBRARY_PATH="$PWD"` (set this varible to where the libraries are) so an application can its find libraries 13 | - Windows: This system searches the program folder and current working directory by default (copy or move files if necessary, e.g. with the `copy /y` command) 14 | - For cross-platform Rust: Not necessary when using `cargo` helpers 15 | 3. Run 16 | - Unix: Run `./` or `gdb ./` to debug 17 | - Windows: Run `.exe` or pop it into WinDbg to debug 18 | - For cross-platform Rust: `cargo run` 19 | 20 | The steps are intentionally designed to be as simple and easy as possible to remove friction from experimentation, even on Windows, thanks to the succinct `build.bat` helper files I made to achieve parity with Unix systems. 21 | -------------------------------------------------------------------------------- /code/glibc/README.md: -------------------------------------------------------------------------------- 1 | # glibc on Linux Code Samples 2 | -------------------------------------------------------------------------------- /code/glibc/atexit/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | $(CC) -o main main.c -fPIC -ldl 3 | $(CC) -shared -o lib1.so lib1.c -fPIC -ldl 4 | $(CC) -shared -o lib2.so lib2.c -fPIC -ldl 5 | 6 | clean: 7 | rm -f main lib1.so lib2.so 8 | 9 | .PHONY: build clean 10 | -------------------------------------------------------------------------------- /code/glibc/atexit/README.md: -------------------------------------------------------------------------------- 1 | # `atexit` Concurrency Analysis 2 | 3 | ## Atexit or Exit Lock 4 | 5 | glibc uses a modular lock made specifically for protecting shared `atexit` data called: `__exit_funcs_lock` 6 | - This shared data it protects is the `exit_function_list` data structure 7 | - The `exit_function_list` linked list is made up of `exit_function` nodes. [The `__new_exitfn` function is responsible for adding new exit functions to this list.](https://elixir.bootlin.com/glibc/glibc-2.38/source/stdlib/cxa_atexit.c#L44) 8 | - glibc [unlocks this lock before calling into an `atexit` routine then relocks it after](https://elixir.bootlin.com/glibc/glibc-2.38/source/stdlib/exit.c#L87-L90) 9 | - More information about [`__exit_funcs_lock`](https://elixir.bootlin.com/glibc/glibc-2.38/source/stdlib/exit.h#L70-L77) 10 | - This lock adheres to the [single-responsibility principle](https://en.wikipedia.org/wiki/Single-responsibility_principle) to create a flexible and performant concurrent design for exit routines 11 | - Tests 12 | - Creating and joining a thread that registers an `atexit` routine is safe (and this new `atexit` function will correctly run) 13 | - Reentering process exit from an `atexit` routine is safe 14 | - Creating a thread that restarts process exit is safe 15 | 16 | Windows UCRT: Critical section lock covering CRT exit (`ucrtbase!common_exit` function), EXE `atexit` (registration and routine execution), and DLL `atexit` (registration and routine execution): `ucrtbase!environ_table+0x70` 17 | - This lock is broad, protecting all of CRT exit, `atexit` registration (EXE or DLL), and `atexit` routine execution (EXE or DLL) 18 | - Set a watchpoint on this lock: `ba r4 @@C++(&((ntdll!_RTL_CRITICAL_SECTION *)@@(ucrtbase!environ_table+0x70))->LockCount)` 19 | - [Source code](https://github.com/huangqinjin/ucrt/blob/master/startup/exit.cpp#L195) (the UCRT is source available) 20 | 21 | Windows MSVCRT: Critical section lock covering CRT exit (`msvcrt!doexit` function), EXE `atexit` (registration and routine execution), and DLL `atexit` (registration and routine execution): `msvcrt!CrtLock_Exit` 22 | - MSVCRT is an ancient C runtime, but it is the one Windows internally links to for applications and libraries that Microsoft includes with the operating system (for backward compatibility reasons), with possibly a few minor exceptions 23 | 24 | ## Loader Lock 25 | 26 | glibc `atexit` routines and `dl_load_lock` 27 | - If `dlclose` is called on a library then its `atexit` routines run from within `dlclose` and thus under `dl_load_lock` 28 | - This appears to be the best possible handling for this scenario since, in the case of a dynamically loaded library being unloaded from memory, it is reasonable to run its exit routines under the loader's protection in case another library concurrently loads that depends on the unloading library (in this case, the exit routines act like module finalizers, which is the best the loader can do since the module is about to be removed from memory) 29 | - In the process exit case, library `atexit` routines are run in the same way the program's `atexit` routines would be run, without `dl_load_lock` 30 | 31 | [GDB Log Viewer](https://html-preview.github.io/?url=https://raw.githubusercontent.com/ElliotKillick/operating-system-design-review/blob/main/code/glibc/atexit/gdb-log.html) 32 | 33 | When calling `atexit` from an EXE, UCRT uses the process-wide `atexit` table 34 | - UCRT calls `ucrtbase!crt_atexit` which uses this table: `ucrtbase!__acrt_atexit_table` 35 | - `atexit` routines run as part of CRT exit, with the loader exit coming later, thus these routines are run without loader lock 36 | - **Exception:** An `atexit` routine registered by a Windows EXE can still run in the module destructors of the CRT library, and therefore under loader lock, [if process exit occurs by calling the Windows API `ExitProcess` function](https://devblogs.microsoft.com/oldnewthing/20160930-00/?p=94425), as opposed to the CRT `exit` function or the `main` function returning normally. 37 | 38 | When calling `atexit` from a DLL, UCRT uses the the module's local `atexit` table 39 | - On MSVC, with the UCRT, `_onexit` calls `ucrtbase!register_onexit_function` which uses this table: `ucrtbase!module_local_atexit_table` 40 | - This `atexit` routine runs as part of the CRT's module destructors ([just after its `DLL_PROCESS_DETACH`](/code/windows/dll-init-order-test/exe-test.c)) and thus under loader lock 41 | 42 | MSVC, when compiling for the UCRT, secretly creates [a stub that internally branches to either call the CRT `atexit` with the process-wide table or a compiled-in DLL `atexit` using a module-local table](https://elliotonsecurity.com/perfect-dll-hijacking/atexit-onexit-disassembly.png) that the CRT can read from, the latter of which runs as part of the CRT module destructors. 43 | 44 | ## Conclusion 45 | 46 | The modular design of glibc process exit allows glibc to unlock the `atexit` lock before calling into our `atexit` routine and then relock it after. Unlocking here is really nice because it means deadlocks due to another thread calling `atexit` while a thread is executing an `atexit` routine cannot happen. In the process exit case, `atexit` routines created by a library do not run under `dl_load_lock`. In the `dlclose` case, `atexit` routines do run under `dl_load_lock`. The high-quality concurrent design of glibc process exit makes its best effort to run `atexit` routines lock-free. 47 | 48 | In contrast, Windows' more rigid approach to locking creates a number of plausible deadlock scenarios. `atexit` routines run from a DLL always runs under loader lock. Such deadlock scenarios become more common in combination with the Windows API's common practice of unexpectedly creating new threads, the architecture's prioritization of heavyweight processes with many DLLs over separate processes, and the tightly coupled nature in which it all works together. 49 | -------------------------------------------------------------------------------- /code/glibc/atexit/lib1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void func1() { 6 | puts("Running atexit handler of library 1!"); 7 | asm("int3"); 8 | } 9 | 10 | __attribute__((constructor)) 11 | void init() { 12 | puts("Library 1 loaded successfully!"); 13 | atexit(func1); 14 | } 15 | -------------------------------------------------------------------------------- /code/glibc/atexit/lib2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void func2() { 6 | puts("Running atexit handler of library 2 (dlclose invocation)!"); 7 | asm("int3"); 8 | } 9 | 10 | __attribute__((constructor)) 11 | void init() { 12 | puts("Library 2 loaded successfully!"); 13 | atexit(func2); 14 | } 15 | -------------------------------------------------------------------------------- /code/glibc/atexit/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void* handle2; 6 | 7 | void programFunc() { 8 | puts("Running atexit handler of program!"); 9 | asm("int3"); 10 | dlclose(handle2); 11 | } 12 | 13 | int main() { 14 | // Atexit handlers are typically run in the reverse order they're registered in (i.e. without dlclose) 15 | void* handle1 = dlopen("lib1.so", RTLD_LAZY); 16 | handle2 = dlopen("lib2.so", RTLD_LAZY); 17 | atexit(programFunc); 18 | } 19 | -------------------------------------------------------------------------------- /code/glibc/data-symbol/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | # Optimize build so the disassembly is like what MSVC outputs 3 | $(CC) -g -shared -o lib1.so lib1.c -fPIC -O3 4 | $(CC) -g -o main main.c -fPIC -O3 -L. -l1 5 | 6 | clean: 7 | rm -f main lib1.so 8 | 9 | .PHONY: build clean 10 | -------------------------------------------------------------------------------- /code/glibc/data-symbol/README.md: -------------------------------------------------------------------------------- 1 | # Data Symbol Experiment 2 | 3 | Linking with and printing a data symbol from a library. 4 | 5 | Note that a data symbol cannot be lazily linked like a function symbol can be. 6 | 7 | See the [Windows equivalent](/code/windows/data-export/README.md) experiment. 8 | 9 | ## Internal View 10 | 11 | Glibc dynamic linking uses fast RIP-relative addressing: 12 | 13 | ``` 14 | (gdb) disassemble /r get_shared_variable_address 15 | Dump of assembler code for function get_shared_variable_address: 16 | 0x0000000000401160 <+0>: 48 8b 05 79 2e 00 00 mov rax,QWORD PTR [rip+0x2e79] # 0x403fe0 17 | 0x0000000000401167 <+7>: c3 ret 18 | End of assembler dump. 19 | ``` 20 | -------------------------------------------------------------------------------- /code/glibc/data-symbol/lib1.c: -------------------------------------------------------------------------------- 1 | // Symbols in the module scope are visible by default 2 | // In practice, we would expose functions along with this, typically only once set, state variable 3 | int shared_variable = 42; 4 | -------------------------------------------------------------------------------- /code/glibc/data-symbol/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Dynamically link to test library 4 | extern int shared_variable; 5 | 6 | int* get_shared_variable_address() { 7 | //__asm__("int3"); 8 | return &shared_variable; 9 | } 10 | 11 | int main() { 12 | printf("shared_variable = %d\n", *get_shared_variable_address()); 13 | } 14 | -------------------------------------------------------------------------------- /code/glibc/dlopen-init-interruption/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | $(CC) -g -shared -o lib1.so lib1.c -fPIC 3 | # Intentionally give lib2 a circular dependency because lib1 also dynamically loads lib2 4 | $(CC) -g -shared -o lib2.so lib2.c -fPIC -L. -l1 5 | $(CC) -g -o main main.c -fPIC -L. -l1 6 | 7 | clean: 8 | rm -f main lib1.so lib2.so 9 | 10 | .PHONY: build clean 11 | -------------------------------------------------------------------------------- /code/glibc/dlopen-init-interruption/README.md: -------------------------------------------------------------------------------- 1 | # `dlopen` Initialization Interruption Experiment 2 | 3 | We evaluate how the loader responds to module initialization being interrupted by a constructor in that module dynamically loading a library that holds a circular dependency on the currently initializing module. 4 | 5 | See the [Windows equivalent](/code/windows/loadlibrary-init-circular-dependency/README.md) experiment. 6 | 7 | ## Result 8 | 9 | ``` 10 | Start: Library 1, init1_1 11 | Start: Library 2, init2_1 12 | Library 1, func1_1 13 | End: Library 2, init2_1 14 | End: Library 1, init1_1 15 | Library 1, init1_2 16 | Library 1, init1_3 17 | ``` 18 | 19 | The glibc loader did not go back and initialize the other constructors in library 1 before running a library 2 initializer. Technically, there is no correct behavior here because the circular dependency means taking either approach in initialization order could lead to unexpected results or crash the process. However, I would consider it "safer" for the loader to first fully initialize all the other constructors in library 1 upon calling into `dlopen` instead of initializing library 2 first. I find this approach "safer" because library 1 being fully initialized other than the one constructor calling `dlopen` seems like a probabilistically less error-prone approach than starting library 2 initialization while multiple constructors in library 1 are fully uninitialized. 20 | 21 | Note that this discussion on the per-routine safety of module initialization interruption is only relevant to begin with because the ELF executable format distinctly exposes each routine in a module using the `.init_array` section. 22 | 23 | **Conclusion:** The GNU loader does not utilize its knowledge of individual initialization routines to help mitigate the poor outcomes of circular dependenices on module initialization. Not trying to compensate for developer stupidity (since circular dependencies are always wrong), potentially at the cost of extra checks that could impact performance for everyone else, is a reasonable choice. 24 | -------------------------------------------------------------------------------- /code/glibc/dlopen-init-interruption/lib1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | __attribute__((constructor)) 5 | void init1_1() { 6 | puts("Start: Library 1, init1_1"); 7 | 8 | // Dynamically load a library with a circular dependency on the currently initializing library 9 | void* lib = dlopen("lib2.so", RTLD_NOW); 10 | if (!lib) { 11 | fprintf(stderr, "%s\n", dlerror()); 12 | return; 13 | } 14 | 15 | puts("End: Library 1, init1_1"); 16 | } 17 | 18 | __attribute__((constructor)) 19 | void init1_2() { 20 | puts("Library 1, init1_2"); 21 | } 22 | 23 | __attribute__((constructor)) 24 | void init1_3() { 25 | puts("Library 1, init1_3"); 26 | } 27 | 28 | 29 | void func1_1() { 30 | puts("Library 1, func1_1"); 31 | } 32 | -------------------------------------------------------------------------------- /code/glibc/dlopen-init-interruption/lib2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern void func1_1(); 5 | 6 | __attribute__((constructor)) 7 | void init2_1() { 8 | puts("Start: Library 2, init2_1"); 9 | 10 | // The other constructors in library 1 still will not initialize before library 2 initialization even if we dynamically load library 1 explicitly 11 | //void* lib1 = dlopen("lib1.so", RTLD_NOW); 12 | //if (!lib1) { 13 | // fprintf(stderr, "%s\n", dlerror()); 14 | // return; 15 | //} 16 | 17 | func1_1(); 18 | 19 | puts("End: Library 2, init2_1"); 20 | } 21 | -------------------------------------------------------------------------------- /code/glibc/dlopen-init-interruption/main.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 0; 3 | } 4 | -------------------------------------------------------------------------------- /code/glibc/dlopen-noload/initialization/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | $(CC) -g -shared -o lib3.so lib3.c -fPIC 3 | $(CC) -g -shared -o lib2.so lib2.c -fPIC 4 | $(CC) -g -shared -o lib1.so lib1.c -fPIC -L. -l2 -l3 5 | $(CC) -g -o main main.c -fPIC -L. -l1 6 | 7 | clean: 8 | rm -f main lib1.so lib2.so lib3.so 9 | 10 | .PHONY: build clean 11 | -------------------------------------------------------------------------------- /code/glibc/dlopen-noload/initialization/README.md: -------------------------------------------------------------------------------- 1 | # `dlopen` with `RTLD_NOLOAD` Initialization Experiment 2 | 3 | For information on this experiment, see the [`GetProcAddress` Can Perform Module Initialization](../..#getprocaddress-can-perform-module-initialization) section. 4 | 5 | ## Dependency Graph 6 | 7 | ``` 8 | .---------. 9 | | Program | 10 | '---------' 11 | | 12 | v 13 | .-----------. 14 | | Library 1 | 15 | '-----------' 16 | | 17 | .--------'--------. 18 | | | 19 | | .-----------. 20 | | | Library 3 | 21 | | '-----------' 22 | | | 23 | | .---------------' 24 | | | 25 | v v 26 | .-----------. 27 | | Library 2 | 28 | '-----------' 29 | ``` 30 | 31 | Both dependencies of Library 1, Library 3 initializes before Library 2 because we specify Library 3 after Library 2 in the `Makefile` (i.e. `-l2 -l3`, not `-l3 -l2`). This initialization order is what we want for our test scenario. At run-time, a module constructor of Library 3 creates a dependency on Library 2 through dynamic loading with the `RTLD_NOLOAD` flag. The edge connecting Library 3 to Library 2 is what, in technical terms, turns this dependency tree into a dependency [graph](https://en.wikipedia.org/wiki/Graph_theory), or a dependency directed acyclic graph (DAG), to be more precise. 32 | 33 | ## Test Result 34 | 35 | ``` 36 | Start: Library 3 initialization 37 | Library 2 initialization 38 | Still inside: Libary 3 initialization 39 | Library 3 got handle to Library 2: 0x7f0d75c66c40 40 | Library 1 loaded successfully! 41 | ``` 42 | 43 | Amazing, the GNU loader sees the dependency Library 3 makes at run-time by calling `dlopen("lib2.so", RTLD_NOLOAD)` from its module constructor and correctly knows to initialize Library 2 before giving back a module handle to it, even when specifying `RTLD_NOLOAD`! 44 | -------------------------------------------------------------------------------- /code/glibc/dlopen-noload/initialization/lib1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Differing external function names matters when dynamic linking due to ELF having a flat symbol namespace 4 | __attribute__((constructor)) 5 | void init1() { 6 | puts("Library 1 loaded successfully!"); 7 | } 8 | -------------------------------------------------------------------------------- /code/glibc/dlopen-noload/initialization/lib2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Differing external function names matters when dynamic linking due to ELF having a flat symbol namespace 4 | __attribute__((constructor)) 5 | void init2() { 6 | puts("Library 2 initialization"); 7 | } 8 | -------------------------------------------------------------------------------- /code/glibc/dlopen-noload/initialization/lib3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Differing external function names matters when dynamic linking due to ELF having a flat symbol namespace 5 | __attribute__((constructor)) 6 | void init3() { 7 | puts("Start: Library 3 initialization"); 8 | 9 | // Library 2 is loaded in memory, but it hasn't initialized yet 10 | // Test what happens when we try to get a handle to the uninitialized library 11 | // 12 | // Note that specifying one of RTLD_LAZY or RTLD_NOW is a requirement (otherwise, we get an "Invalid argument" error) 13 | // Specify RTLD_LAZY because, as indicated by the manual, specifying RTLD_NOW can promote the library to that binding (promotion can also occur with other flags) and we only want to test RTLD_NOLOAD 14 | void* lib = dlopen("lib2.so", RTLD_LAZY | RTLD_NOLOAD); 15 | 16 | puts("Still inside: Libary 3 initialization"); 17 | 18 | if (!lib) { 19 | fprintf(stderr, "%s\n", dlerror()); 20 | return; 21 | } 22 | 23 | printf("Library 3 got handle to Library 2: %p\n", lib); 24 | } 25 | -------------------------------------------------------------------------------- /code/glibc/dlopen-noload/initialization/main.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 0; 3 | } 4 | -------------------------------------------------------------------------------- /code/glibc/dlopen-noload/reference-count/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | $(CC) -g -o main main.c -fPIC -ldl 3 | $(CC) -g -shared -o lib1.so lib1.c -fPIC 4 | 5 | clean: 6 | rm -f main lib1.so 7 | 8 | .PHONY: build clean 9 | -------------------------------------------------------------------------------- /code/glibc/dlopen-noload/reference-count/README.md: -------------------------------------------------------------------------------- 1 | # `dlopen` with `RTLD_NOLOAD` Reference Counting Experiment 2 | 3 | For information on this experiment, see the [`GetProcAddress` Can Perform Module Initialization](../..#getprocaddress-can-perform-module-initialization) section. 4 | 5 | ## Test Result 6 | 7 | ``` 8 | Library 1 opened successfully! 9 | ``` 10 | 11 | If the program's `dlclose` of Library 1 following its call to `dlopen` with the `RTLD_NOLOAD` flag actually unloaded the library from memory, we would have seen the message `Library 1 closed successfully!`. Reference counting confirmed. 12 | -------------------------------------------------------------------------------- /code/glibc/dlopen-noload/reference-count/lib1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | __attribute__((constructor)) 4 | void init1() { 5 | puts("Library 1 opened successfully!"); 6 | } 7 | 8 | __attribute__((destructor)) 9 | void fini1() { 10 | puts("Library 1 closed successfully!"); 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /code/glibc/dlopen-noload/reference-count/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | void* lib = dlopen("lib1.so", RTLD_LAZY); 8 | dlopen("lib1.so", RTLD_LAZY | RTLD_NOLOAD); 9 | dlclose(lib); 10 | 11 | // Exit without calling module destructors at process exit 12 | _exit(EXIT_SUCCESS); 13 | } 14 | -------------------------------------------------------------------------------- /code/glibc/dlopen-reentrancy/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | $(CC) -o main main.c -fPIC -ldl 3 | $(CC) -shared -o lib1.so lib1.c -fPIC 4 | $(CC) -shared -o lib2.so lib2.c -fPIC 5 | 6 | clean: 7 | rm -f main lib1.so lib2.so 8 | 9 | .PHONY: build clean 10 | -------------------------------------------------------------------------------- /code/glibc/dlopen-reentrancy/README.md: -------------------------------------------------------------------------------- 1 | # `dlopen` Reentrancy Experiment 2 | 3 | Loading a library from a library's module initializer (i.e. reentering the loader or recursive loading). 4 | 5 | [GDB Log Viewer](https://html-preview.github.io/?url=https://raw.githubusercontent.com/ElliotKillick/operating-system-design-review/blob/main/code/glibc/dlopen-reentrancy/gdb-log.html) 6 | -------------------------------------------------------------------------------- /code/glibc/dlopen-reentrancy/gdb-cmds.txt: -------------------------------------------------------------------------------- 1 | run 2 | gef 3 | code 3 1 4 | # ^ We break from constructor of lib1 before doing dlopen on lib2 (using an int3 trap) 5 | break rtld_lock_default_lock_recursive 6 | break rtld_lock_default_unlock_recursive 7 | continue 8 | print _rtld_global._dl_load_lock 9 | print _rtld_global._dl_load_lock.mutex.__data.__count 10 | # ^ This count increments/decrements with every loader lock/unlock 11 | x/5i rtld_lock_default_lock_recursive 12 | print lock 13 | # ^ "lock" is a local variable to this function 14 | x/1wx lock+0x4 15 | x/1wx $rdi+0x4 16 | nexti 17 | print _rtld_global._dl_load_lock.mutex.__data.__count 18 | x/1wx lock+0x4 19 | x/1wx $rdi+0x4 20 | backtrace 21 | delete breakpoints 1 2 22 | continue 23 | exit 24 | -------------------------------------------------------------------------------- /code/glibc/dlopen-reentrancy/gdb-log.html: -------------------------------------------------------------------------------- 1 | This log is from a Debian 11 machine with GNU ld 2.31 (according to ld --version command): 2 | 3 |
gdb ./main
  4 | Reading symbols from ./main...
  5 | (No debugging symbols found in ./main)
  6 | (gdb) run
  7 | Starting program: /home/user/Documents/dlopen-lock/main
  8 | Library 1 loaded successfully!
  9 | 
 10 | Program received signal SIGTRAP, Trace/breakpoint trap.
 11 | 0x00007ffff7fc8126 in func () from /home/user/Documents/dlopen-lock/lib1.so
 12 | (gdb) gef
 13 | GEF for linux ready, type `gef' to start, `gef config' to configure
 14 | 91 commands loaded for GDB 10.1.90.20210103-git using Python engine 3.9
 15 | [*] 5 commands could not be loaded, run `gef missing` to know why.
 16 | [+] Configuration from '/home/user/.gef.rc' restored
 17 | [+] 35 extra commands added from '/home/user/Documents/gef-extras/scripts'
 18 | gef➤  code 3 1
 19 | ─────────────────────────────────────────────────────────────────────── code:x86:64 ────
 20 |    0x7ffff7fc8125 <func+16>        int3   
 21 |  → 0x7ffff7fc8126 <func+17>        mov    esi, 0x1
 22 |    0x7ffff7fc812b <func+22>        lea    rdi, [rip+0xeed]        # 0x7ffff7fc901f
 23 |    0x7ffff7fc8132 <func+29>        call   0x7ffff7fc8030 <dlopen@plt>
 24 | ────────────────────────────────────────────────────────────────────────────────────────
 25 | gef➤  # ^ We break from constructor of lib1 before doing dlopen on lib2 (using an int3 trap)
 26 | gef➤  break rtld_lock_default_lock_recursive
 27 | Breakpoint 1 at 0x7ffff7fd30e0: file rtld.c, line 841.
 28 | gef➤  break rtld_lock_default_unlock_recursive
 29 | Breakpoint 2 at 0x7ffff7fd30f0: file rtld.c, line 847.
 30 | gef➤  continue
 31 | Continuing.
 32 | 
 33 | Breakpoint 1, rtld_lock_default_lock_recursive (lock=0x7ffff7ffd968 <_rtld_global+2312>) at rtld.c:841
 34 | 841	rtld.c: No such file or directory.
 35 | gef➤  print _rtld_global._dl_load_lock
 36 | $1 = {
 37 |   mutex = {
 38 |     __data = {
 39 |       __lock = 0x0,
 40 |       __count = 0x1,
 41 |       __owner = 0x0,
 42 |       __nusers = 0x0,
 43 |       __kind = 0x1,
 44 |       __spins = 0x0,
 45 |       __elision = 0x0,
 46 |       __list = {
 47 |         __prev = 0x0,
 48 |         __next = 0x0
 49 |       }
 50 |     },
 51 |     __size = "\000\000\000\000\001", '\000' <repeats 11 times>, "\001", '\000' <repeats 22 times>,
 52 |     __align = 0x100000000
 53 |   }
 54 | }
 55 | gef➤  print _rtld_global._dl_load_lock.mutex.__data.__count
 56 | $2 = 0x1
 57 | gef➤  # ^ This count increments/decrements with every loader lock/unlock
 58 | gef➤  x/5i rtld_lock_default_lock_recursive
 59 | => 0x7ffff7fd30e0 <rtld_lock_default_lock_recursive>:	add    DWORD PTR [rdi+0x4],0x1
 60 |    0x7ffff7fd30e4 <rtld_lock_default_lock_recursive+4>:	ret    
 61 |    0x7ffff7fd30e5:	data16 nop WORD PTR cs:[rax+rax*1+0x0]
 62 |    0x7ffff7fd30f0 <rtld_lock_default_unlock_recursive>:	sub    DWORD PTR [rdi+0x4],0x1
 63 |    0x7ffff7fd30f4 <rtld_lock_default_unlock_recursive+4>:	ret    
 64 | gef➤  print lock
 65 | $3 = (void *) 0x7ffff7ffd968 <_rtld_global+2312>
 66 | gef➤  x/1wx lock+0x4
 67 | 0x7ffff7ffd96c <_rtld_global+2316>:	0x00000001
 68 | gef➤  x/1wx $rdi+0x4
 69 | 0x7ffff7ffd96c <_rtld_global+2316>:	0x00000001
 70 | gef➤  nexti
 71 | 841	in rtld.c
 72 | gef➤  print _rtld_global._dl_load_lock.mutex.__data.__count
 73 | $4 = 0x2
 74 | gef➤  x/1wx lock+0x4
 75 | 0x7ffff7ffd96c <_rtld_global+2316>:	0x00000002
 76 | gef➤  x/1wx $rdi+0x4
 77 | 0x7ffff7ffd96c <_rtld_global+2316>:	0x00000002
 78 | gef➤  backtrace
 79 | #0  rtld_lock_default_lock_recursive (lock=0x7ffff7ffd968 <_rtld_global+2312>) at rtld.c:841
 80 | #1  0x00007ffff7fe5896 in _dl_open (file=0x7ffff7fc901f "lib2.so", mode=0x80000001, caller_dlopen=0x7ffff7fc8137 <func+34>, nsid=0xfffffffffffffffe, argc=0x1, argv=0x7fffffffe828, env=0x7fffffffe838) at dl-open.c:786
 81 | #2  0x00007ffff7fa0258 in dlopen_doit (a=a@entry=0x7fffffffe110) at dlopen.c:66
 82 | #3  0x00007ffff7f00a90 in __GI__dl_catch_exception (exception=exception@entry=0x7fffffffe0b0, operate=0x7ffff7fa0200 <dlopen_doit>, args=0x7fffffffe110) at dl-error-skeleton.c:208
 83 | #4  0x00007ffff7f00b4f in __GI__dl_catch_error (objname=0x7ffff7fa40f0 <last_result+16>, errstring=0x7ffff7fa40f8 <last_result+24>, mallocedp=0x7ffff7fa40e8 <last_result+8>, operate=<optimized out>, args=<optimized out>) at dl-error-skeleton.c:227
 84 | #5  0x00007ffff7fa0a65 in _dlerror_run (operate=operate@entry=0x7ffff7fa0200 <dlopen_doit>, args=args@entry=0x7fffffffe110) at dlerror.c:170
 85 | #6  0x00007ffff7fa02e4 in __dlopen (file=<optimized out>, mode=<optimized out>) at dlopen.c:87
 86 | #7  0x00007ffff7fc8137 in func () from /home/user/Documents/dlopen-lock/lib1.so
 87 | #8  0x00007ffff7fe1fe2 in call_init (l=<optimized out>, argc=argc@entry=0x1, argv=argv@entry=0x7fffffffe828, env=env@entry=0x7fffffffe838) at dl-init.c:72
 88 | #9  0x00007ffff7fe20e9 in call_init (env=0x7fffffffe838, argv=0x7fffffffe828, argc=0x1, l=<optimized out>) at dl-init.c:30
 89 | #10 _dl_init (main_map=0x5555555592e0, argc=0x1, argv=0x7fffffffe828, env=0x7fffffffe838) at dl-init.c:119
 90 | #11 0x00007ffff7f00aed in __GI__dl_catch_exception (exception=<optimized out>, operate=<optimized out>, args=<optimized out>) at dl-error-skeleton.c:182
 91 | #12 0x00007ffff7fe6058 in dl_open_worker (a=a@entry=0x7fffffffe4d0) at dl-open.c:758
 92 | #13 0x00007ffff7f00a90 in __GI__dl_catch_exception (exception=0x7fffffffe4b0, operate=0x7ffff7fe5ca0 <dl_open_worker>, args=0x7fffffffe4d0) at dl-error-skeleton.c:208
 93 | #14 0x00007ffff7fe58fa in _dl_open (file=0x555555556014 "lib1.so", mode=0x80000001, caller_dlopen=0x55555555516d <main+21>, nsid=0xfffffffffffffffe, argc=0x1, argv=0x7fffffffe4b0, env=0x7fffffffe838) at dl-open.c:837
 94 | #15 0x00007ffff7fa0258 in dlopen_doit (a=a@entry=0x7fffffffe6f0) at dlopen.c:66
 95 | #16 0x00007ffff7f00a90 in __GI__dl_catch_exception (exception=exception@entry=0x7fffffffe690, operate=0x7ffff7fa0200 <dlopen_doit>, args=0x7fffffffe6f0) at dl-error-skeleton.c:208
 96 | #17 0x00007ffff7f00b4f in __GI__dl_catch_error (objname=0x7ffff7fa40f0 <last_result+16>, errstring=0x7ffff7fa40f8 <last_result+24>, mallocedp=0x7ffff7fa40e8 <last_result+8>, operate=<optimized out>, args=<optimized out>) at dl-error-skeleton.c:227
 97 | #18 0x00007ffff7fa0a65 in _dlerror_run (operate=operate@entry=0x7ffff7fa0200 <dlopen_doit>, args=args@entry=0x7fffffffe6f0) at dlerror.c:170
 98 | #19 0x00007ffff7fa02e4 in __dlopen (file=<optimized out>, mode=<optimized out>) at dlopen.c:87
 99 | #20 0x000055555555516d in main ()
100 | gef➤  delete breakpoints 1 2
101 | gef➤  continue
102 | Continuing.
103 | Library 2 loaded successfully from constructor of Library 1!
104 | [Inferior 1 (process 12003) exited normally]
105 | gef➤  exit
106 | -------------------------------------------------------------------------------- /code/glibc/dlopen-reentrancy/lib1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | __attribute__((constructor)) 5 | void func() { 6 | puts("Library 1 loaded successfully!"); 7 | 8 | //asm("int3"); 9 | dlopen("lib2.so", RTLD_LAZY); 10 | } 11 | -------------------------------------------------------------------------------- /code/glibc/dlopen-reentrancy/lib2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | __attribute__((constructor)) 4 | void func() { 5 | puts("Library 2 loaded successfully from constructor of Library 1!"); 6 | //asm ("int3"); 7 | } 8 | -------------------------------------------------------------------------------- /code/glibc/dlopen-reentrancy/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | dlopen("lib1.so", RTLD_LAZY); 5 | } 6 | -------------------------------------------------------------------------------- /code/glibc/dlopen-thread-join-dlopen/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | $(CC) -g -o main main.c -ldl -fPIC -lpthread 3 | $(CC) -g -shared -o lib1.so lib1.c -fPIC -lpthread 4 | $(CC) -g -shared -o lib2.so lib2.c -fPIC -lpthread 5 | 6 | clean: 7 | rm -f main lib1.so lib2.so 8 | 9 | .PHONY: build clean 10 | -------------------------------------------------------------------------------- /code/glibc/dlopen-thread-join-dlopen/README.md: -------------------------------------------------------------------------------- 1 | # `dlopen` Thread Join `dlopen` Experiment 2 | 3 | Spawning a thread from a module initializer, waiting on the thread to exit, meanwhile loading another library in the new thread. 4 | 5 | This experiment is our control. It will predictably deadlock because the first `dlopen` function already holds `dl_load_lock` for the thread, then we create a new thread that tries to run `dlopen` necessitating `dl_load_lock` for that thread, and wait for the new thread to exit from the first thread thus causing a deadlock. 6 | 7 | Unix architecture does not heavily rely on dynamic library library or multithreading though, so in practice this deadlock scenario does not occur. Some libc distributions such as musl even purposely do not support dynamic library loading. 8 | 9 | The only way a loader, while still supporting dynamic library loading, may be able to avoid deadlocking in this scenario would be to [make the loader's module initialization phase MT-safe](../#investigating-the-idea-of-mt-safe-library-initialization). However, implementing such a mechanism is likely not worth the synchronization overhead increase it would comes with given the existing architectural advantages of Unix. 10 | -------------------------------------------------------------------------------- /code/glibc/dlopen-thread-join-dlopen/gdb-log.txt: -------------------------------------------------------------------------------- 1 | This log is taken when the deadlock occurs (^C at program hang). It's from a Fedora 38 machine with GNU ld 2.39 (according to ld --version command). 2 | 3 | ======================================================= 4 | Thread 1 waiting for thread 2 to exit with pthread_join 5 | ======================================================= 6 | 7 | Library 1 loaded successfully! 8 | [New Thread 0x7ffff7dcc6c0 (LWP 4788)] 9 | Thread started from library constructor! 10 | ^C 11 | Thread 1 "main" received signal SIGINT, Interrupt. 12 | 0x00007ffff7e59219 in __futex_abstimed_wait_common64 (private=128, cancel=true, abstime=0x0, op=265, expected=4788, futex_word=0x7ffff7dcc990) 13 | at futex-internal.c:57 14 | 57 return INTERNAL_SYSCALL_CANCEL (futex_time64, futex_word, op, expected, 15 | (gdb) backtrace 16 | #0 0x00007ffff7e59219 in __futex_abstimed_wait_common64 (private=128, cancel=true, abstime=0x0, op=265, expected=4788, futex_word=0x7ffff7dcc990) 17 | at futex-internal.c:57 18 | #1 __futex_abstimed_wait_common (futex_word=futex_word@entry=0x7ffff7dcc990, expected=4788, clockid=clockid@entry=0, abstime=abstime@entry=0x0, 19 | private=private@entry=128, cancel=cancel@entry=true) at futex-internal.c:87 20 | #2 0x00007ffff7e5929f in __GI___futex_abstimed_wait_cancelable64 (futex_word=futex_word@entry=0x7ffff7dcc990, expected=, 21 | clockid=clockid@entry=0, abstime=abstime@entry=0x0, private=private@entry=128) at futex-internal.c:139 22 | #3 0x00007ffff7e5e623 in __pthread_clockjoin_ex (threadid=140737351829184, thread_return=0x0, clockid=0, abstime=0x0, block=) 23 | at pthread_join_common.c:102 24 | #4 0x00007ffff7fbe1a8 in func () at lib1.c:23 25 | #5 0x00007ffff7fcef77 in call_init (env=0x7fffffffe0e8, argv=0x7fffffffe0d8, argc=1, l=) at dl-init.c:90 26 | #6 call_init (l=, argc=1, argv=0x7fffffffe0d8, env=0x7fffffffe0e8) at dl-init.c:27 27 | #7 0x00007ffff7fcf06d in _dl_init (main_map=0x4052f0, argc=1, argv=0x7fffffffe0d8, env=0x7fffffffe0e8) at dl-init.c:137 28 | #8 0x00007ffff7fcb5c2 in __GI__dl_catch_exception (exception=exception@entry=0x0, operate=operate@entry=0x7ffff7fd5c30 , 29 | args=args@entry=0x7fffffffdb00) at dl-catch.c:211 30 | #9 0x00007ffff7fd5bcc in dl_open_worker (a=a@entry=0x7fffffffdcb0) at dl-open.c:808 31 | #10 0x00007ffff7fcb523 in __GI__dl_catch_exception (exception=exception@entry=0x7fffffffdc90, operate=operate@entry=0x7ffff7fd5b30 , 32 | args=args@entry=0x7fffffffdcb0) at dl-catch.c:237 33 | #11 0x00007ffff7fd5f44 in _dl_open (file=0x402010 "lib1.so", mode=, caller_dlopen=0x40113e , nsid=, argc=1, 34 | argv=0x7fffffffe0d8, env=0x7fffffffe0e8) at dl-open.c:884 35 | #12 0x00007ffff7e58714 in dlopen_doit (a=a@entry=0x7fffffffdf60) at dlopen.c:56 36 | #13 0x00007ffff7fcb523 in __GI__dl_catch_exception (exception=exception@entry=0x7fffffffdea0, operate=0x7ffff7e586b0 , args=0x7fffffffdf60) 37 | at dl-catch.c:237 38 | #14 0x00007ffff7fcb679 in _dl_catch_error (objname=0x7fffffffdf08, errstring=0x7fffffffdf10, mallocedp=0x7fffffffdf07, operate=, 39 | args=) at dl-catch.c:256 40 | #15 0x00007ffff7e581f3 in _dlerror_run (operate=operate@entry=0x7ffff7e586b0 , args=args@entry=0x7fffffffdf60) at dlerror.c:138 41 | #16 0x00007ffff7e587cf in dlopen_implementation (dl_caller=, mode=, file=) at dlopen.c:71 42 | #17 ___dlopen (file=, mode=) at dlopen.c:81 43 | #18 0x000000000040113e in main () at main.c:4 44 | (gdb) info threads 45 | Id Target Id Frame 46 | * 1 Thread 0x7ffff7dcd740 (LWP 4784) "main" 0x00007ffff7e59219 in __futex_abstimed_wait_common64 (private=128, cancel=true, abstime=0x0, op=265, 47 | expected=4788, futex_word=0x7ffff7dcc990) at futex-internal.c:57 48 | 2 Thread 0x7ffff7dcc6c0 (LWP 4788) "main" futex_wait (private=0, expected=2, futex_word=0x7ffff7ffda08 <_rtld_local+2568>) 49 | at ../sysdeps/nptl/futex-internal.h:146 50 | 51 | ======================================================= 52 | Thread 2 deadlocks while trying to acquire dl_load_lock 53 | ======================================================= 54 | 55 | (gdb) thread 2 56 | [Switching to thread 2 (Thread 0x7ffff7dcc6c0 (LWP 4788))] 57 | #0 futex_wait (private=0, expected=2, futex_word=0x7ffff7ffda08 <_rtld_local+2568>) at ../sysdeps/nptl/futex-internal.h:146 58 | 146 int err = lll_futex_timed_wait (futex_word, expected, NULL, private); 59 | (gdb) backtrace 60 | #0 futex_wait (private=0, expected=2, futex_word=0x7ffff7ffda08 <_rtld_local+2568>) at ../sysdeps/nptl/futex-internal.h:146 61 | #1 __GI___lll_lock_wait (futex=futex@entry=0x7ffff7ffda08 <_rtld_local+2568>, private=0) at lowlevellock.c:49 62 | #2 0x00007ffff7e5feb7 in lll_mutex_lock_optimized (mutex=0x7ffff7ffda08 <_rtld_local+2568>) at pthread_mutex_lock.c:48 63 | #3 ___pthread_mutex_lock (mutex=0x7ffff7ffda08 <_rtld_local+2568>) at pthread_mutex_lock.c:128 64 | #4 0x00007ffff7fd5edb in _dl_open (file=0x7ffff7fbf029 "lib2.so", mode=-2147483647, caller_dlopen=0x7ffff7fbe160 , nsid=-2, argc=1, 65 | argv=0x7fffffffe0d8, env=0x7fffffffe0e8) at dl-open.c:830 66 | #5 0x00007ffff7e58714 in dlopen_doit (a=a@entry=0x7ffff7dcbe70) at dlopen.c:56 67 | #6 0x00007ffff7fcb523 in __GI__dl_catch_exception (exception=exception@entry=0x7ffff7dcbdb0, operate=0x7ffff7e586b0 , args=0x7ffff7dcbe70) 68 | at dl-catch.c:237 69 | #7 0x00007ffff7fcb679 in _dl_catch_error (objname=0x7ffff7dcbe18, errstring=0x7ffff7dcbe20, mallocedp=0x7ffff7dcbe17, operate=, 70 | args=) at dl-catch.c:256 71 | #8 0x00007ffff7e581f3 in _dlerror_run (operate=operate@entry=0x7ffff7e586b0 , args=args@entry=0x7ffff7dcbe70) at dlerror.c:138 72 | #9 0x00007ffff7e587cf in dlopen_implementation (dl_caller=, mode=, file=) at dlopen.c:71 73 | #10 ___dlopen (file=, mode=) at dlopen.c:81 74 | #11 0x00007ffff7fbe160 in thread () at lib1.c:10 75 | #12 0x00007ffff7e5c947 in start_thread (arg=) at pthread_create.c:444 76 | #13 0x00007ffff7ee2870 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81 77 | (gdb) up 4 78 | #4 0x00007ffff7fd5edb in _dl_open (file=0x7ffff7fbf029 "lib2.so", mode=-2147483647, caller_dlopen=0x7ffff7fbe160 , nsid=-2, argc=1, 79 | argv=0x7fffffffe0d8, env=0x7fffffffe0e8) at dl-open.c:830 80 | 830 __rtld_lock_lock_recursive (GL(dl_load_lock)); 81 | (gdb) list 82 | 825 if ((mode & RTLD_BINDING_MASK) == 0) 83 | 826 /* One of the flags must be set. */ 84 | 827 _dl_signal_error (EINVAL, file, NULL, N_("invalid mode for dlopen()")); 85 | 828 86 | 829 /* Make sure we are alone. */ 87 | 830 __rtld_lock_lock_recursive (GL(dl_load_lock)); <-- MY NOTE: We deadlock here! 88 | 831 89 | 832 if (__glibc_unlikely (nsid == LM_ID_NEWLM)) 90 | 833 { 91 | 834 /* Find a new namespace. */ 92 | (gdb) 93 | (gdb) set print pretty 94 | (gdb) print _rtld_global._dl_load_lock 95 | $1 = { 96 | mutex = { 97 | __data = { 98 | __lock = 2, <-- MY NOTE: Contention 99 | __count = 1, 100 | __owner = 4784, 101 | __nusers = 1, 102 | __kind = 1, 103 | __spins = 0, 104 | __elision = 0, 105 | __list = { 106 | __prev = 0x0, 107 | __next = 0x0 108 | } 109 | }, 110 | __size = "\002\000\000\000\001\000\000\000\260\022\000\000\001\000\000\000\001", '\000' , 111 | __align = 4294967298 112 | } 113 | } 114 | (gdb) print _rtld_global._dl_load_write_lock 115 | $2 = { 116 | mutex = { 117 | __data = { 118 | __lock = 0, 119 | __count = 0, 120 | __owner = 0, 121 | __nusers = 0, 122 | __kind = 1, 123 | __spins = 0, 124 | __elision = 0, 125 | __list = { 126 | __prev = 0x0, 127 | __next = 0x0 128 | } 129 | }, 130 | __size = '\000' , "\001", '\000' , 131 | __align = 0 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /code/glibc/dlopen-thread-join-dlopen/lib1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void* thread() 6 | { 7 | puts("Thread started from library constructor!"); 8 | 9 | // Load lib2 from thread spawned by lib1 constructor 10 | dlopen("lib2.so", RTLD_LAZY); 11 | 12 | return NULL; 13 | } 14 | 15 | __attribute__((constructor)) 16 | void func() { 17 | puts("Library 1 loaded successfully!"); 18 | 19 | pthread_t thread1; 20 | pthread_create(&thread1, NULL, &thread, NULL); 21 | 22 | //asm("int3"); 23 | 24 | // Wait for thread to exit 25 | pthread_join(thread1, NULL); 26 | } 27 | -------------------------------------------------------------------------------- /code/glibc/dlopen-thread-join-dlopen/lib2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | __attribute__((constructor)) 4 | void func() { 5 | puts("Library 2 loaded successfully on new thread spawned by constructor of Library 1!"); 6 | //asm ("int3"); 7 | } 8 | -------------------------------------------------------------------------------- /code/glibc/dlopen-thread-join-dlopen/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | dlopen("lib1.so", RTLD_LAZY); 5 | } 6 | -------------------------------------------------------------------------------- /code/glibc/dlopen-thread-join/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | $(CC) -o main main.c -fPIC -ldl -lpthread 3 | $(CC) -shared -o lib1.so lib1.c -fPIC -lpthread 4 | 5 | clean: 6 | rm -f main lib1.so 7 | 8 | .PHONY: build clean 9 | -------------------------------------------------------------------------------- /code/glibc/dlopen-thread-join/README.md: -------------------------------------------------------------------------------- 1 | # `dlopen` Thread Join Experiment 2 | 3 | Spawning a thread and waiting for its creation then exit from a module initializer. 4 | -------------------------------------------------------------------------------- /code/glibc/dlopen-thread-join/lib1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void* thread() 6 | { 7 | puts("Thread started from library constructor!"); 8 | 9 | return NULL; 10 | } 11 | 12 | __attribute__((constructor)) 13 | void func() { 14 | puts("Library 1 loaded successfully!"); 15 | 16 | pthread_t thread1; 17 | pthread_create(&thread1, NULL, &thread, NULL); 18 | 19 | //asm("int3"); 20 | 21 | // Wait for thread to exit 22 | pthread_join(thread1, NULL); 23 | } 24 | -------------------------------------------------------------------------------- /code/glibc/dlopen-thread-join/lib2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | __attribute__((constructor)) 4 | void func() { 5 | puts("Library 2 loaded successfully from constructor of Library 1!"); 6 | //asm ("int3"); 7 | } 8 | -------------------------------------------------------------------------------- /code/glibc/dlopen-thread-join/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | dlopen("lib1.so", RTLD_LAZY); 5 | } 6 | -------------------------------------------------------------------------------- /code/glibc/dlopen/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | $(CC) -g -o main main.c -fPIC -ldl 3 | $(CC) -g -shared -o lib1.so lib1.c -fPIC 4 | $(CC) -g -shared -o lib2.so lib2.c -fPIC 5 | 6 | clean: 7 | rm -f main lib1.so lib2.so 8 | 9 | .PHONY: build clean 10 | -------------------------------------------------------------------------------- /code/glibc/dlopen/README.md: -------------------------------------------------------------------------------- 1 | # `dlopen` Experiment 2 | 3 | We set write watchpoints on the `dl_load_lock` and `dl_load_write_lock` data, then load two libraries to check how often and where in the code these locks are acquired. 4 | 5 | [GDB Log Viewer](https://html-preview.github.io/?url=https://raw.githubusercontent.com/ElliotKillick/operating-system-design-review/blob/main/code/glibc/dlopen/gdb-log.html) 6 | 7 | ## Debugging Notes 8 | 9 | Ensure you have glibc debug symbols and source code downloaded. Fedora's GDB automatically downloads symbols and source code. On Debian, you must install the `libc6-dbg` (you may need to enable the debug source in your `/etc/apt/sources.list`) and `glibc-source` packages. 10 | 11 | When printing a `backtrace` in GDB (or generally printing a variable name), you may see `` in the output for some function parameter values. Consistently seeing the values of these variables requires compiling glibc with no optimizations enabled (i.e. setting the `-O0` or `-Og` GCC option). You could also set a breakpoint where the variable is initially set or passed into a function to see its value before optimizations remove it. Typically, you don't need to know the value of these `` variables, though, so you can ignore them. 12 | 13 | In the backtrace of the GDB log file, you can see entries to functions like `dlerror_run`, [`_dl_catch_error`](https://elixir.bootlin.com/glibc/glibc-2.38/source/elf/dl-catch.c#L251) and `__GI__dl_catch_exception` (`_dl_catch_exception` in the code). These aren't indicative of an error occurring. Rather, these functions merely set up an error handler and perform handling to catch exceptions if an [error](https://elixir.bootlin.com/glibc/glibc-2.38/source/sysdeps/generic/ldsodefs.h#L847)/[exception occurs in the dynamic linker](https://elixir.bootlin.com/glibc/glibc-2.38/source/sysdeps/generic/ldsodefs.h#L837). The error codes are conformant to [errno](https://man7.org/linux/man-pages/man3/errno.3.html). Error codes returned by the dynamic linker includes [`ENOMEM`](https://elixir.bootlin.com/glibc/glibc-2.38/source/elf/dl-load.c#L729) (out of memory) or file access error codes (like `ENOENT` or `EACCES`). Dynamic linker functions may also pass `0` as the `errno` to indicate an [error specific to the dynamic linker's functionality](https://elixir.bootlin.com/glibc/glibc-2.38/source/elf/dl-load.c#L2236). In either case, an exception occurs, thus stopping the dynamic linker from proceeding and continuing execution in `_dl_catch_exception` (called by `_dl_catch_error`). A programmer can determine if `dlopen` failed by checking for a `NULL` return value and then get an error message with [`dlerror`](https://pubs.opengroup.org/onlinepubs/009696799/functions/dlerror.html). 14 | -------------------------------------------------------------------------------- /code/glibc/dlopen/lib1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | __attribute__((constructor)) 4 | void func() { 5 | puts("Library 1 loaded successfully!"); 6 | } 7 | -------------------------------------------------------------------------------- /code/glibc/dlopen/lib2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | __attribute__((constructor)) 4 | void func() { 5 | puts("Library 2 loaded successfully!"); 6 | } 7 | -------------------------------------------------------------------------------- /code/glibc/dlopen/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | dlopen("lib1.so", RTLD_LAZY); 5 | dlopen("lib2.so", RTLD_LAZY); 6 | } 7 | -------------------------------------------------------------------------------- /code/glibc/lazy-bind/dynamic-link/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | # Make sure to set LD_LIBRARY_PATH before compiling this (otherwise it fails) 3 | # -l specifies "lib1"/"lib2" without the "lib" prefix 4 | # GCC compiles with lazy binding by default but let's make sure by passing "-z lazy" to the linker 5 | # The GNU loader also defaults to lazily resolving the lazy binding compiled into the program 6 | # Library 1 depends on library 2 so library 1 can call a call a function from library 2 7 | # Verify dependency chain with "ldd" command 8 | $(CC) -g -shared -o lib2.so lib2.c -fPIC 9 | $(CC) -g -shared -o lib1.so lib1.c -fPIC -L. -l2 -Wl,-z,lazy 10 | $(CC) -g -o main main.c -fPIC -L. -l1 -Wl,-z,lazy 11 | 12 | clean: 13 | rm -f main lib1.so lib2.so 14 | 15 | .PHONY: build clean 16 | -------------------------------------------------------------------------------- /code/glibc/lazy-bind/dynamic-link/lib1.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void func2(); 4 | 5 | // Differing external function names matters when dynamic linking due to ELF having a flat symbol namespace 6 | __attribute__((constructor)) 7 | void init1() { 8 | puts("Library 1 loaded successfully!"); 9 | 10 | // Call lazily binded export 11 | // Whether lazy binding actually happens depends on your loader 12 | // In GDB, stepping in shows the loader lazily resolving func2 before calling it 13 | func2(); 14 | } 15 | -------------------------------------------------------------------------------- /code/glibc/lazy-bind/dynamic-link/lib2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Differing external function names matters when dynamic linking due to ELF having a flat symbol namespace 4 | __attribute__((constructor)) 5 | void init2() { 6 | puts("Library 2 loaded successfully!"); 7 | } 8 | 9 | void func2() { 10 | puts("Library 2 export lazily binded and called from library 1 constructor successfully!"); 11 | } 12 | -------------------------------------------------------------------------------- /code/glibc/lazy-bind/dynamic-link/main.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 0; 3 | } 4 | -------------------------------------------------------------------------------- /code/glibc/lazy-bind/dynamic-load/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | $(CC) -g -o main main.c -fPIC -ldl 3 | $(CC) -g -shared -o lib1.so lib1.c -fPIC -ldl 4 | $(CC) -g -shared -o lib2.so lib2.c -fPIC 5 | 6 | clean: 7 | rm -f main lib1.so lib2.so 8 | 9 | .PHONY: build clean 10 | -------------------------------------------------------------------------------- /code/glibc/lazy-bind/dynamic-load/lib1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | __attribute__((constructor)) 5 | void init1() { 6 | puts("Library 1 loaded successfully!"); 7 | 8 | // Load library with lazy binding 9 | void* lib = dlopen("lib2.so", RTLD_LAZY); 10 | 11 | if (!lib) { 12 | fprintf(stderr, "%s\n", dlerror()); 13 | return; 14 | } 15 | 16 | // Lazily resolve library export 17 | void (*func2)() = dlsym(lib, "func2"); 18 | puts("Library 2 export lazily loaded successfully!"); 19 | 20 | // Call export 21 | func2(); 22 | } 23 | -------------------------------------------------------------------------------- /code/glibc/lazy-bind/dynamic-load/lib2.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | __attribute__((constructor)) 4 | void init2() { 5 | puts("Library 2 loaded successfully!"); 6 | } 7 | 8 | __attribute__((visibility ("default"))) 9 | void func2() { 10 | puts("Library 2 export called successfully!"); 11 | } 12 | -------------------------------------------------------------------------------- /code/glibc/lazy-bind/dynamic-load/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | void* lib = dlopen("lib1.so", RTLD_LAZY); 6 | } 7 | -------------------------------------------------------------------------------- /code/glibc/thread-performance/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | $(CC) -o main thread-test.c -O3 -pthread 3 | 4 | clean: 5 | rm -f main 6 | 7 | .PHONY: build clean 8 | -------------------------------------------------------------------------------- /code/glibc/thread-performance/thread-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define NUM_THREADS 10000 6 | 7 | void* dummy_thread(void* arg) { 8 | // Thread does nothing and exits 9 | return NULL; 10 | } 11 | 12 | int main() { 13 | pthread_t threads[NUM_THREADS]; 14 | printf("Creating %d threads...\n", NUM_THREADS); 15 | 16 | int thread_count = 0; 17 | 18 | struct timespec start, end; 19 | clock_gettime(CLOCK_MONOTONIC, &start); 20 | 21 | while (thread_count < NUM_THREADS) { 22 | if (pthread_create(&threads[thread_count], NULL, dummy_thread, NULL) != 0) { 23 | break; 24 | } 25 | 26 | ++thread_count; 27 | } 28 | 29 | // Fight off zombie horde 30 | for (int i = 0; i < thread_count; ++i) { 31 | pthread_join(threads[i], NULL); 32 | } 33 | 34 | clock_gettime(CLOCK_MONOTONIC, &end); 35 | 36 | if (thread_count < NUM_THREADS) { 37 | fprintf(stderr, "Error: Unable to create thread %d\n", thread_count + 1); 38 | return EXIT_FAILURE; 39 | } 40 | 41 | double elapsed = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9; 42 | printf("Time taken to create and join %d threads (seconds): %.2f\n", thread_count, elapsed); 43 | 44 | return EXIT_SUCCESS; 45 | } 46 | -------------------------------------------------------------------------------- /code/windows/README.md: -------------------------------------------------------------------------------- 1 | # Windows Code Samples 2 | -------------------------------------------------------------------------------- /code/windows/data-export/README.md: -------------------------------------------------------------------------------- 1 | # Data Export Experiment 2 | 3 | Linking with and printing a data export from a library. 4 | 5 | Note that a data export cannot be lazily linked like a function export can be. 6 | 7 | Windows has supported dynamic linking of [data exports since Windows NT 3.1]( http://web.archive.org/web/20070219042535/https://support.microsoft.com/kb/90530) (the first Windows NT release), the same as for function exports. 8 | 9 | See the [glibc equivalent](/code/glibc/data-symbol/README.md) experiment. 10 | 11 | ## Internal View 12 | 13 | Windows dynamic linking uses fast RIP-relative addressing (although, MASM abstracts that information out of the disassembly): 14 | 15 | ``` 16 | 0:000> uf exe_test!get_shared_variable_address 17 | exe_test!get_shared_variable_address [C:\Users\user\Documents\data-export\exe-test.c @ 6]: 18 | 6 00007ff7`8ba41000 48 8b 05 a9 21 00 00 mov rax,qword ptr [exe_test!_imp_shared_variable (00007ff7`8ba431b0)] 19 | 9 00007ff7`8ba41007 c3 ret 20 | ``` 21 | -------------------------------------------------------------------------------- /code/windows/data-export/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | cl dll-test.c /DUNICODE /D_UNICODE /MD /LD /Zi /DEBUG /link /INCREMENTAL:NO 7 | cl exe-test.c dll-test.lib /DUNICODE /D_UNICODE /MD /Zi /DEBUG /link /INCREMENTAL:NO 8 | 9 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 10 | if %ERRORLEVEL% equ 9009 ( 11 | echo Compiler not found. Opening Visual Studio developer environment... 12 | set "VSCMD_START_DIR=%cd%" 13 | set "VSCMD_SKIP_SENDTELEMETRY=1" 14 | rem Find the latest x64 Developer CMD version then open it 15 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 16 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 17 | ) 18 | -------------------------------------------------------------------------------- /code/windows/data-export/dll-test.c: -------------------------------------------------------------------------------- 1 | // Export the variable 2 | // In practice, we would export functions along with this, typically only once set, state variable 3 | __declspec(dllexport) int shared_variable = 42; 4 | -------------------------------------------------------------------------------- /code/windows/data-export/exe-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Dynamically link to test library 4 | __declspec(dllimport) int shared_variable; 5 | 6 | int* get_shared_variable_address() { 7 | //__debugbreak(); 8 | return &shared_variable; 9 | } 10 | 11 | int main() { 12 | printf("shared_variable = %d\n", *get_shared_variable_address()); 13 | } 14 | -------------------------------------------------------------------------------- /code/windows/dll-init-order-test/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | cl dll-test.cpp /DUNICODE /D_UNICODE /MD /LD /Zi /DEBUG /link /INCREMENTAL:NO 7 | cl exe-test.c dll-test.lib /DUNICODE /D_UNICODE /MD /Zi /DEBUG /link /INCREMENTAL:NO 8 | 9 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 10 | if %ERRORLEVEL% equ 9009 ( 11 | echo Compiler not found. Opening Visual Studio developer environment... 12 | set "VSCMD_START_DIR=%cd%" 13 | set "VSCMD_SKIP_SENDTELEMETRY=1" 14 | rem Find the latest x64 Developer CMD version then open it 15 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 16 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 17 | ) 18 | -------------------------------------------------------------------------------- /code/windows/dll-init-order-test/dll-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Layout DllMain before C++ initializtion in the code to try getting it called first (it won't be, DllMain is always initialized last) 5 | 6 | // Yes, puts/printf is unsafe from a module destructor on Windows because it acquires the CRT stdio critical section lock, which could be orphaned 7 | 8 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { 9 | switch (fdwReason) { 10 | case DLL_PROCESS_ATTACH: 11 | puts("DllMain: DLL_PROCESS_ATTACH"); 12 | break; 13 | case DLL_PROCESS_DETACH: 14 | puts("DllMain: DLL_PROCESS_DETACH"); 15 | break; 16 | } 17 | 18 | return TRUE; 19 | } 20 | 21 | struct A { 22 | A() { 23 | puts("A: C++ module constructor"); 24 | } 25 | 26 | void test() { 27 | puts("A: Test method"); 28 | } 29 | 30 | ~A() { 31 | puts("A: C++ module destructor"); 32 | } 33 | }; 34 | 35 | // Create a new object that is a global instance of the A data type (struct, class, etc.) 36 | // We create object "a" before object "b" in the code, so object "a" will initialize first 37 | A a; 38 | 39 | // A programmer may want to export this object (using __declspec(dllexport)), so other modules can access it 40 | // Or, a programmer may create an object at the module scope as the C++ way to address cross-cutting concerns 41 | struct B { 42 | B() { 43 | puts("B: C++ module constructor"); 44 | } 45 | 46 | void test() { 47 | puts("B: Test method"); 48 | } 49 | 50 | ~B() { 51 | puts("B: C++ module destructor"); 52 | } 53 | } b; // Create an instance here, although we could also do it the same way we do for A and even make multiple instances 54 | 55 | EXTERN_C __declspec(dllexport) void DummyExport() { 56 | // Exported function that does nothing so we can dynamically link 57 | puts("Test export"); 58 | } 59 | -------------------------------------------------------------------------------- /code/windows/dll-init-order-test/exe-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Dynamically link to test DLL 4 | __declspec(dllimport) void DummyExport(); 5 | 6 | int main() { 7 | // Call dummy export so our link to the DLL isn't optimized out 8 | DummyExport(); 9 | 10 | return EXIT_SUCCESS; 11 | } 12 | 13 | // Program output: 14 | // A: C++ module constructor 15 | // B: C++ module constructor 16 | // DllMain: DLL_PROCESS_ATTACH 17 | // Test export 18 | // DllMain: DLL_PROCESS_DETACH 19 | // B: C++ module destructor 20 | // A: C++ module destructor 21 | -------------------------------------------------------------------------------- /code/windows/dll-process-detach-test-harness/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | cl dll-process-detach-test-harness.c /DUNICODE /D_UNICODE /MD /Zi /DEBUG /link ntdll.lib shell32.lib /INCREMENTAL:NO 7 | 8 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 9 | if %ERRORLEVEL% equ 9009 ( 10 | echo Compiler not found. Opening Visual Studio developer environment... 11 | set "VSCMD_START_DIR=%cd%" 12 | set "VSCMD_SKIP_SENDTELEMETRY=1" 13 | rem Find the latest x64 Developer CMD version then open it 14 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 15 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 16 | ) 17 | -------------------------------------------------------------------------------- /code/windows/dll-worker-thread-return-value-rust/executable/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "executable" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | library = { path = "../library" } 8 | -------------------------------------------------------------------------------- /code/windows/dll-worker-thread-return-value-rust/executable/src/main.rs: -------------------------------------------------------------------------------- 1 | use library::hello; 2 | 3 | fn main() { 4 | hello(); 5 | } 6 | -------------------------------------------------------------------------------- /code/windows/dll-worker-thread-return-value-rust/library/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "library" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | crate-type = ["dylib"] 8 | -------------------------------------------------------------------------------- /code/windows/dll-worker-thread-return-value-rust/library/src/lib.rs: -------------------------------------------------------------------------------- 1 | // WORK IN PROGRESS 2 | 3 | // We use module constructors and destructors because: 4 | // 1. We want to avoid lazy initialization 5 | // - Rust is a system-level programming language and we do not want our system-level code to incur the overhead that comes with constantly evaluating an atomic or synchronized "is initialized" condition as is the case with a Rust lazy_static or once_cell 6 | // - Our subsystem should be fully initialized when it is done loading 7 | // 2. Our module is one that presents significant cross-cutting concerns 8 | // - As a result, we need a predictable initialization time to avoid unexpected results 9 | // 3. Timing matters 10 | // - Some initialization tasks require I/O, which could lead to priority inversion if they run at an unexpeted time 11 | // - Managing priority is especially important when building a real-time application 12 | // - Timing differences due to a lazy initialization slowdown can be used in a timing attack as a side-channel for leaking information 13 | // 4. Two-phase initialization is an anti-pattern 14 | // - The same can reasonably extend to lazy initialization, although lazy initialization trades a partial initialization danger for a perfomance hit by constantly checking for initialization on every use 15 | // 5. Operating systems should be sane 16 | // - It is not like any operating system would be broken and unhinged enough to go around terminating threads, right? Right? 17 | 18 | pub fn hello() { 19 | println!("Hello from the Rust dynamic library!"); 20 | } 21 | -------------------------------------------------------------------------------- /code/windows/dll-worker-thread-return-value/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | cl lib.cpp /DUNICODE /D_UNICODE /MD /LD /EHsc /Zi /DEBUG /link /INCREMENTAL:NO 7 | cl exe-test.c lib.lib /DUNICODE /D_UNICODE /MD /Zi /DEBUG /link /INCREMENTAL:NO 8 | 9 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 10 | if %ERRORLEVEL% equ 9009 ( 11 | echo Compiler not found. Opening Visual Studio developer environment... 12 | set "VSCMD_START_DIR=%cd%" 13 | set "VSCMD_SKIP_SENDTELEMETRY=1" 14 | rem Find the latest x64 Developer CMD version then open it 15 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 16 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 17 | ) 18 | -------------------------------------------------------------------------------- /code/windows/dll-worker-thread-return-value/exe-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Dynamically link to DLL 4 | __declspec(dllimport) void DummyExport(); 5 | 6 | int main() { 7 | // Call dummy export so our link to the DLL isn't optimized out 8 | DummyExport(); 9 | 10 | return EXIT_SUCCESS; 11 | } 12 | -------------------------------------------------------------------------------- /code/windows/dll-worker-thread-return-value/lib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Cross-platform library 6 | #ifdef _WIN32 7 | #define EXPORT __declspec(dllexport) 8 | #else 9 | #define EXPORT __attribute__((visibility("default"))) 10 | #endif 11 | 12 | // Exported function that does nothing so we can dynamically link 13 | extern "C" EXPORT void DummyExport() { 14 | //puts("Test export"); 15 | } 16 | 17 | // Library worker thread 18 | // RAII-style structure to manage worker thread 19 | // 20 | // Our thread produces a return value, it's a common idea: 21 | // https://stackoverflow.com/questions/7686939/c-simple-return-value-from-stdthread 22 | // https://stackoverflow.com/questions/1314155/returning-a-value-from-thread 23 | struct Worker { 24 | std::thread thread; // The thread itself 25 | int result; // Return value produced by the thread (this could also be a pointer to a heap allocation or a new custom return type structure on the heap, but for demonstration purposes we will simply return an integer 26 | 27 | // Constructor to start the thread 28 | Worker() { 29 | // A simple lambda, it captures this instance so we can easily access its members within the thread (a bit of functional programming) 30 | // Microsoft likes creating threads that run an anonymous lambda function and often uses lambdas within the Windows API (despite anonymous functions being a debugging eyesore) 31 | thread = std::thread([this]() { 32 | // In the thread, we do some work (maybe taking client requests, it could be anything). When we're all done, the thread returns a value. 33 | // Here, we calculate the Answer to the Ultimate Question of Life, the Universe, and Everything 34 | // This calculation is computationally expensive, so it's going to take a few seconds (even without sleeping, we sometimes lose the race condition) 35 | std::this_thread::sleep_for(std::chrono::seconds(3)); 36 | result = 21 + 21; 37 | }); 38 | } 39 | 40 | // Destructor to ensure the thread is joined 41 | ~Worker() { 42 | if (thread.joinable()) { 43 | // Thread joins back when it is done executing and exits 44 | // However, on Windows, NtTerminateProcess can abruptly kill the thread before it exits, which includes signaling the thread object as if had exited normally 45 | thread.join(); 46 | // 47 | // Oh no, our thread was terminated before it returned its result (a race condition)! 48 | // Now we are printing uninitialized memory and we do not get the correct answer!!! 49 | // 50 | // OPTION ONE: 51 | // The result is just an interger stored in the .data section of our library, we will incorrectly get a zero! 52 | // If we make decisions based on the incorrect return value, then we could also crash due to taking a wrong branch! 53 | // 54 | // OPTION TWO: 55 | // The Worker structure (and consequently the result) was dynamically allocated on the heap and now we are about to print some juicy initialized heap memory (bye bye ASLR)! 56 | // If result is a pointer to a result (e.g. int* or a pointer to a result structure type) then we would dereference that uninitialized memory thus causing a crash or saying hello to security vulnerabilities! 57 | std::cout << result << std::endl; 58 | std::cout << &result << std::endl; // See where the structure is in memory (for debugging purposes) 59 | } 60 | } 61 | }; 62 | 63 | #undef OPTION_ONE 64 | 65 | #ifdef OPTION_ONE 66 | // OPTION ONE 67 | // Library subsystem scope 68 | Worker worker; 69 | 70 | // Example outputs: 71 | // C:\...>exe-test.exe 72 | // 0 73 | // 00007FFC0D6C50F0 74 | // C:\...>exe-test.exe 75 | // 0 76 | // 00007FFC0D6C50F0 77 | // C:\...>exe-test.exe 78 | // 0 79 | // 00007FFC0D6C50F0 80 | #else 81 | // OPTION TWO 82 | // Function for dynamically creating workers within the library subsystem scope 83 | // Smart pointer ensures cleanup at process exit 84 | // We may want to dynamically query some configuration data or the number of CPU cores to know how many worker threads we should create 85 | // In this case, we will only create one worker for demonstration purposes 86 | std::unique_ptr worker(new Worker()); // Create Worker structure on the heap 87 | 88 | // Example outputs: 89 | // C:\...>exe-test.exe 90 | // 1802661751 91 | // 000001F2C3135A20 92 | // C:\...>exe-test.exe 93 | // 544433516 94 | // 0000021FC7755AA0 95 | // C:\...>exe-test.exe 96 | // -1008860848 97 | // 000001A8C3DF5830 98 | #endif 99 | -------------------------------------------------------------------------------- /code/windows/event-experiment/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | cl event-experiment.c /DUNICODE /D_UNICODE /MD /Zi /DEBUG /link /INCREMENTAL:NO 7 | 8 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 9 | if %ERRORLEVEL% equ 9009 ( 10 | echo Compiler not found. Opening Visual Studio developer environment... 11 | set "VSCMD_START_DIR=%cd%" 12 | set "VSCMD_SKIP_SENDTELEMETRY=1" 13 | rem Find the latest x64 Developer CMD version then open it 14 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 15 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 16 | ) 17 | -------------------------------------------------------------------------------- /code/windows/event-experiment/event-experiment.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // The typical usage pattern for an event is to signal when something completes and unsignal while something is incomplete 4 | // For example, the LdrpInitCompleteEvent loader event signals when loader initialization is complete 5 | // https://learn.microsoft.com/en-us/windows/win32/sync/event-objects 6 | // https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-resetevent#remarks 7 | 8 | int main() { 9 | // Create an unsignaled auto-reset event 10 | HANDLE myEvent = CreateEvent(NULL, FALSE, FALSE, L"MyEvent"); 11 | 12 | if (myEvent == 0) 13 | return 1; 14 | 15 | // Set event 16 | SetEvent(myEvent); 17 | __debugbreak(); 18 | 19 | // Event: Set -> Waiting 20 | // Execution proceeds 21 | WaitForSingleObject(myEvent, INFINITE); 22 | 23 | // Event is waiting, so execution waits 24 | // WinDbg command: !handle MyEvent ff 25 | // We expectedly hang here 26 | WaitForSingleObject(myEvent, INFINITE); 27 | 28 | CloseHandle(myEvent); 29 | 30 | // When an auto-reset event is set, execution only proceeds for a SINGLE waiting thread before the event waits. 31 | // This behavior means an auto-reset event performs mutual exclusion between threads, similar to a critical section. 32 | // 33 | // Unlike a critical section, though, an auto-reset event doesn't support recursive acquisition on the same thread. 34 | // 35 | // Generally, another difference between an event object and a critical section is that the former has no owning thread, which means one thread can lock an event and another thread can unlock it. 36 | // Lastly, an event object is inter-process while a critical section is intra-process. 37 | } 38 | -------------------------------------------------------------------------------- /code/windows/fls-experiment/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | cl fls-experiment.c /DUNICODE /D_UNICODE /MD /Zi /DEBUG /link /INCREMENTAL:NO 7 | 8 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 9 | if %ERRORLEVEL% equ 9009 ( 10 | echo Compiler not found. Opening Visual Studio developer environment... 11 | set "VSCMD_START_DIR=%cd%" 12 | set "VSCMD_SKIP_SENDTELEMETRY=1" 13 | rem Find the latest x64 Developer CMD version then open it 14 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 15 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 16 | ) 17 | -------------------------------------------------------------------------------- /code/windows/fls-experiment/fls-experiment.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // An FLS callback may be run manually: 5 | // The public FlsFree function (leads to RtlFlsFree in NTDLL) itself only runs a single FLS callback 6 | // 7 | // FLS callbacks may be run at fiber exit: 8 | // The public DeleteFiber KERNEL32 function (leads to DeleteFiber in KERNELBASE) calls RtlProcessFlsData in NTDLL 9 | // 10 | // FLS callbacks may be run at thread or process exit: 11 | // Both LdrShutdownThread and LdrShutdownProcess functions call RtlpFlsDataCleanup in NTDLL 12 | 13 | // 14 | // Tests 15 | // 16 | 17 | // Whether to test FLS callbacks at process exit or thread exit (we only test these) 18 | #undef TEST_FLS_CALLBACK_THREAD_EXIT 19 | 20 | #undef TEST_REENTRANT_REGISTRATION_FLS_CALLBACK_HANDLER // FLS callback registration (FlsAlloc) from within an FLS callback handler (RtlpFlsDataCleanup) 21 | #undef TEST_REENTRANT_FLS_CALLBACK_HANDLER // Reenter FLS callback handler (RtlpFlsDataCleanup) 22 | #undef TEST_FLS_CALLBACK_FREE_SELF // FlsFree self 23 | #undef TEST_FLS_CALLBACK_FREE_OTHER // FlsFree other 24 | 25 | // FLS indexes 26 | DWORD flsIdx1; 27 | DWORD flsIdx2; 28 | DWORD flsIdx3; 29 | 30 | // Demo FLS data 31 | // Realistically, this would likely be a pointer to heap-allocated memory 32 | // you want to free or do other cleanup work on at thread termination 33 | INT flsDemoData1; 34 | INT flsDemoData2; 35 | INT flsDemoData3; 36 | 37 | void WINAPI flsCallback3(PVOID flsData) { 38 | puts("flsCallback3"); 39 | } 40 | 41 | void WINAPI flsCallback2(PVOID flsData) { 42 | puts("flsCallback2"); 43 | } 44 | 45 | void WINAPI flsCallback1(PVOID flsData) { 46 | puts("flsCallback1"); 47 | 48 | #ifdef TEST_REENTRANT_REGISTRATION_FLS_CALLBACK_HANDLER 49 | // This FLS callback will be registered (no deadlock) but never run 50 | // RtlpFlsDataCleanup returns without recognizing that we registered a new FLS callback within one of the previously run FLS callback routines 51 | // Therefore, registering an FLS callback at process exit is unsupported 52 | // 53 | // RESULTS (PROGRAM OUTPUT): 54 | // flsCallback1 55 | // flsCallback2 56 | flsIdx3 = FlsAlloc(flsCallback3); 57 | FlsSetValue(flsIdx3, &flsDemoData3); 58 | #endif 59 | 60 | #ifdef TEST_REENTRANT_FLS_CALLBACK_HANDLER 61 | // Summary: The RtlpFlsDataCleanup function isn't reentrant 62 | #ifndef TEST_FLS_CALLBACK_THREAD_EXIT 63 | // ExitProcess -> RtlExitUserProcess calls NtTerminateProcess with the first argument of zero for the SECOND time (because we're already exiting) 64 | // This triggers the kernel to terminate our process immediately. 65 | // As a result, RtlpFlsDataCleanup never gets a chance to run again, and since the function won't be looping around anymore, only the current FLS callback is ever run. 66 | // Process exit isn't reentrant, effectively making FLS callbacks also non-reentrant in this case. 67 | // 68 | // RESULTS (PROGRAM OUTPUT): 69 | // flsCallback1 70 | ExitProcess(0); 71 | #else 72 | // RESULTS (PROGRAM OUTPUT): 73 | // flsCallback1 74 | // flsCallback1 75 | // flsCallback1 76 | // flsCallback1 77 | // flsCallback1 78 | // The same FLS callback will run multiple times! If we were doing a free() here then that's a double free... 79 | // ... This goes on forever until the program crashes due to stack overflow! (although, that's really our fault) ... 80 | // Note: Calling ExitProcess from a thread's FLS callback will similarly cause the FLS callback to run for a second time 81 | ExitThread(0); 82 | #endif 83 | #endif 84 | 85 | #ifdef TEST_FLS_CALLBACK_FREE_SELF 86 | // Deadlock on FLS per-allocation SRW lock while trying to acquire it in write/exclusive mode. 87 | // I like deadlocking here better than double-freeing. 88 | // However, I think it would be equally okay to set some state (non-atomic because this is per-thread) that remembers whether this callback previously called, and in that case, ignore any attempts to call it again (no deadlock). 89 | // But, perhaps it's best to enforce correctness on the caller's side. 90 | // 91 | // RESULTS (PROGRAM OUTPUT): 92 | // flsCallback1 93 | // *hang* 94 | FlsFree(flsIdx1); 95 | #endif 96 | 97 | #ifdef TEST_FLS_CALLBACK_FREE_OTHER 98 | // This works great! 99 | // 100 | // RESULTS (PROGRAM OUTPUT): 101 | // flsCallback1 102 | // flsCallback2 103 | FlsFree(flsIdx2); 104 | #endif 105 | } 106 | 107 | void flsTest() { 108 | flsIdx1 = FlsAlloc(flsCallback1); 109 | FlsSetValue(flsIdx1, &flsDemoData1); 110 | flsIdx2 = FlsAlloc(flsCallback2); 111 | FlsSetValue(flsIdx2, &flsDemoData2); 112 | } 113 | 114 | int main() { 115 | #ifndef TEST_FLS_CALLBACK_THREAD_EXIT 116 | flsTest(); 117 | #else 118 | DWORD threadId; 119 | HANDLE thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)flsTest, NULL, 0, &threadId); 120 | if (thread) 121 | WaitForSingleObject(thread, INFINITE); 122 | #endif 123 | } 124 | 125 | // Conclusion 126 | // 127 | // The FLS callback handler isn't reentrant: 128 | // - It's not supported to register an FLS callback from within an FLS callback (the new callback will never be run) 129 | // - It's unsafe or not supported to reenter the FLS callback handler by exiting the thread/process again 130 | // Freeing an FLS index (FlsFree) from within an FLS callback (during RtlpFlsDataCleanup) is safe. Unless you free your own FLS index again, then you deadlock. 131 | // 132 | // FLS callbacks run under loader lock during process exit; however, not during thread exit. 133 | // During process exit, PEB_LDR_DATA.ShutdownInProgress is true while running FLS callbacks. 134 | // 135 | // When running in an FLS callback, one must also be aware that previous FLS callbacks may have free'd or otherwise cleaned up the current thread's resources. 136 | // Surprisingly, FLS callbacks aren't run in the reverse order they were registered in (like, for example, DLL_PROCESS_DETACH notifications are at process exit) thus making a use-after-free scenario more likely. 137 | // 138 | // Each FLS allocation is run under a per-allocation (FlsAlloc) SRW lock which RtlpFlsFree acquires in write/exclusive mode and RtlpFlsDataCleanup acquires in read/shared mode. 139 | // The difference in how the FLS callback handlers acquire this lock explains why reentering the current FLS callback with FlsFree deadlocks while RtlpFlsDataCleanup is able to reenter the same callback multiple times. 140 | // 141 | // There's also a global RtlpFlsContext SRW lock (at ntdll!RtlpFlsContext+0x0) which RtlpFlsFree acquires in read/shared mode and RtlpFlsDataCleanup acquires in write/exclusive mode. 142 | // This lock protects the global FLS state. However, a callback never runs under this lock (verified for both RtlpFlsFree and RtlpFlsDataCleanup functions). 143 | 144 | 145 | // More FLS Information 146 | // 147 | // At ntdll!RtlpFlsContext is a global data structure pertaining to FLS. 148 | // I haven't looked into it entirely, but it contains a binary array (RTL_BINARY_ARRAY), an SRW lock, and likely more. 149 | // The binary array likely keeps track of which FLS indexes (or slots) are occupied. 150 | // 151 | // FLS indexes are valid across fiber and thread boundaries, but not across process boundaries: https://learn.microsoft.com/en-us/windows/win32/api/fibersapi/nf-fibersapi-flsalloc#remarks 152 | // There's a process-wide maximum number of FLS indexes (before FlsAlloc returns an FLS_OUT_OF_INDEXES error): https://ntdoc.m417z.com/fls_maximum_available 153 | // 154 | // A thread's TEB stores a pointer to its own FLS data: TEB.FlsData 155 | // TEB.FlsData starts out as null. On first call to the RtlFlsSetValue function, it checks if TEB.FlsData is null, and if so, creates a heap allocation (RtlAllocateHeap) to store all that thread's FLS data. Each FLS value is stored in the binary array contained within that heap allocation. 156 | // 157 | // the FLS data of each thread gets its own heap allocation (RtlAllocateHeap), then individual FLS allocations are stored in the binary array within that heap allocation. 158 | // On first use of TEB.FlsData, RtlFlsSetValue initializes FlsData by setting the new FLS allocation as the first entry in the linked list. The global RtlpFlsContext+0x0 SRW lock is write/exclusively acquired as the new TEB.FlsData gets added to the global/shared FLS state. 159 | // After that, each FLS allocation just gets linked into the TEB.FlsData list. The TEB.FlsData linked list data structure is local state (i.e. only a single thread may access it), so it doesn't require protection. 160 | // 161 | // "Fibers are awful" (at least on Windows, according to the comments), but the callbacks are still useful: https://devblogs.microsoft.com/oldnewthing/20191011-00/?p=102989 162 | -------------------------------------------------------------------------------- /code/windows/library-init-lazy-load/README.md: -------------------------------------------------------------------------------- 1 | # Library Initializer Lazy Loading Experiment 2 | 3 | [Microsoft documentation](https://learn.microsoft.com/en-us/cpp/build/reference/linker-support-for-delay-loaded-dlls) states this in regard to delay loading and `DllMain`: 4 | 5 | > A DLL project that delays the loading of one or more DLLs itself shouldn't call a delay-loaded entry point in `DllMain`. 6 | 7 | Microsoft's reasoning for this statement is likely that Windows and possibly other subsystems often use delay loading as a hack for postponing when a circular dependency is loaded. In practice, the stated target is unachievable because executing code cannot know whether calling an import in another DLL will trigger a delay load somewhere in the dependency chain. The only resolution then is to either do nothing from `DllMain`, or axe delay loading and break dependency cycles. 8 | 9 | Find instances of delay loading in a call stack by looking for the function: [`__delayLoadHelper2`](https://learn.microsoft.com/en-us/cpp/build/reference/understanding-the-helper-function#calling-conventions-parameters-and-return-type) 10 | -------------------------------------------------------------------------------- /code/windows/library-init-lazy-load/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | cl lib1.c /DUNICODE /D_UNICODE /MD /LD /Zi /DEBUG /link advapi32.lib /INCREMENTAL:NO 7 | cl main.c /DUNICODE /D_UNICODE /MD /Zi /DEBUG /link /INCREMENTAL:NO 8 | 9 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 10 | if %ERRORLEVEL% equ 9009 ( 11 | echo Compiler not found. Opening Visual Studio developer environment... 12 | set "VSCMD_START_DIR=%cd%" 13 | set "VSCMD_SKIP_SENDTELEMETRY=1" 14 | rem Find the latest x64 Developer CMD version then open it 15 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 16 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 17 | ) 18 | -------------------------------------------------------------------------------- /code/windows/library-init-lazy-load/lib1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include // For UNLEN 4 | 5 | BOOL printUsername() { 6 | // Buffer to store the username 7 | WCHAR username[UNLEN + 1]; // UNLEN is the maximum length of a username 8 | DWORD username_len = sizeof(username) / sizeof(username[0]); // Size of the buffer 9 | 10 | // Call GetUserNameW to retrieve the username 11 | // This causes advapi32.dll to delay load sspicli.dll 12 | if (GetUserNameW(username, &username_len)) { 13 | wprintf(L"Username: %s\n", username); 14 | } 15 | else { 16 | wprintf(L"Error retrieving username. Error code: %lu\n", GetLastError()); 17 | return 1; 18 | } 19 | 20 | return 0; 21 | } 22 | 23 | BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved) { 24 | if (fdwReason == DLL_PROCESS_ATTACH) { 25 | printUsername(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /code/windows/library-init-lazy-load/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | LoadLibrary(L"lib1.dll"); 5 | 6 | // Output: Username: user 7 | // (or your username) 8 | } 9 | -------------------------------------------------------------------------------- /code/windows/library-init-thread-join/README.md: -------------------------------------------------------------------------------- 1 | # Library Initializer Thread Join Experiment 2 | 3 | Spawning a thread and waiting for its creation then exit from a module initializer or finalizer (the latter being the module subsystem lifetime). 4 | 5 | This experiment deadlocks on the `ntdll!LdrpInitCompleteEvent` event object in the `LdrpInitialize` function during process startup. During process run-time the deadlock will occur a bit later on the `ntdll!LdrpLoadCompleteEvent` event object in the `LdrpInitializeThread` ➜ `LdrpDrainWorkQueue` function (due to running `DLL_THREAD_ATTACH`). If it was possible to still continue then thread creation or exit would further block when acquiring the `ntdll!LdrpLoaderLock` lock in the `LdrpInitializeThread` function also due to running `DLL_THREAD_ATTACH` routines at thread startup or `DLL_THREAD_DETACH` routines at thread exit. 6 | 7 | The loader intertwining with the threading implementation as we see here represents an overextension of the loader because those components should have zero knowledge of each other much less share synchronization. In other words, these subsystems are tightly coupled. 8 | 9 | Specifying the `THREAD_CREATE_FLAGS_SKIP_LOADER_INIT` (new in Windows 10) on `NtCreateThreadEx` can bypass these thread creation and exit blockers. However, I have not seen an occurrence of the Windows API internally spawning a thread with this flag, so these deadlocks remain prevalent. 10 | 11 | The funny thing about these deadlocks is that since Ctrl + C on Windows works by spwaning a thread into a process, they hang your entire terminal indefinitely (you need to whip out Task Manager). It can also lock out some Windows debugging tools that work by remotely spawning a thread to break-in a process or ascertain some information about it. 12 | -------------------------------------------------------------------------------- /code/windows/library-init-thread-join/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | cl dll-test.c /DUNICODE /D_UNICODE /MD /LD /Zi /DEBUG /link /INCREMENTAL:NO 7 | cl exe-test.c dll-test.lib /DUNICODE /D_UNICODE /MD /Zi /DEBUG /link /INCREMENTAL:NO 8 | 9 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 10 | if %ERRORLEVEL% equ 9009 ( 11 | echo Compiler not found. Opening Visual Studio developer environment... 12 | set "VSCMD_START_DIR=%cd%" 13 | set "VSCMD_SKIP_SENDTELEMETRY=1" 14 | rem Find the latest x64 Developer CMD version then open it 15 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 16 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 17 | ) 18 | -------------------------------------------------------------------------------- /code/windows/library-init-thread-join/dll-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | EXTERN_C __declspec(dllexport) void DummyExport() { 4 | // Exported function that does nothing so we can dynamically link 5 | puts("Test export"); 6 | } 7 | 8 | DWORD WINAPI dummy_thread(LPVOID lpParam) { 9 | // Thread does nothing and exits 10 | return 0; 11 | } 12 | 13 | HANDLE myThread; 14 | 15 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { 16 | switch (fdwReason) { 17 | case DLL_PROCESS_ATTACH: 18 | myThread = CreateThread(NULL, 0, dummy_thread, NULL, 0, NULL); 19 | if (myThread) 20 | WaitForSingleObject(myThread, INFINITE); // Deadlock here 21 | break; 22 | case DLL_PROCESS_DETACH: 23 | if (myThread) 24 | //WaitForSingleObject(myThread, INFINITE); // This would also deadlock (thus breaking the library subsystem lifetime for threads) 25 | break; 26 | } 27 | 28 | return TRUE; 29 | } 30 | -------------------------------------------------------------------------------- /code/windows/library-init-thread-join/exe-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Dynamically link to DLL 4 | __declspec(dllimport) void DummyExport(); 5 | 6 | int main() { 7 | // Call dummy export so our link to the DLL isn't optimized out 8 | DummyExport(); 9 | 10 | return EXIT_SUCCESS; 11 | } 12 | -------------------------------------------------------------------------------- /code/windows/loadlibrary-concurrent-work/README.md: -------------------------------------------------------------------------------- 1 | # `LoadLibrary` Concurrent Work Experiment 2 | 3 | Running two library loads concurrently to study the loader's ability for work parallelization. In particular, this experiment verifies whether or not a concurrent `LoadLibrary` will help another `LoadLibrary` in its mapping and snapping work. 4 | 5 | ## Steps 6 | 7 | 1. Disable loader worker threads (CMD command): `reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\exe-test.exe" /v MaxLoaderThreads /t REG_DWORD /d 1 /f` 8 | - This is to keep them from picking up work and messing up our experiment 9 | 2. Set a breakpoint on the `ntdll!LdrpQueueWork` function: `bp ntdll!LdrpQueueWork` 10 | 3. When the first `LoadLibrary` thread hits this breakpoint, continue execution until that function returns: `gu` 11 | 4. Verify `ntdll!LdrpWorkQueue` is not empty: `!list ntdll!LdrpWorkQueue` 12 | 5. Suspend that thread: `~n` 13 | 6. Set a breakpoint on `ntdll!LdrpProcessWork` 14 | 7. Continue execution: `g` 15 | 16 | Thread 2 `LoadLibrary` picking up work from from thread 1 `LoadLibrary`: 17 | 18 | ``` 19 | 0:002> k 20 | # Child-SP RetAddr Call Site 21 | 00 000000aa`a0eff8a8 00007ffa`56b70048 ntdll!LdrpProcessWork 22 | 01 000000aa`a0eff8b0 00007ffa`56b2fad7 ntdll!LdrpDrainWorkQueue+0x184 23 | 02 000000aa`a0eff8f0 00007ffa`56b273e4 ntdll!LdrpLoadDllInternal+0xc3 24 | 03 000000aa`a0eff970 00007ffa`56b26af4 ntdll!LdrpLoadDll+0xa8 25 | 04 000000aa`a0effb20 00007ffa`54522612 ntdll!LdrLoadDll+0xe4 26 | 05 000000aa`a0effc10 00007ff7`4f2b103e KERNELBASE!LoadLibraryExW+0x162 27 | 06 000000aa`a0effc80 00007ffa`56337374 exe_test!loadlibrary_thread_2+0x3e [C:\Users\user\Documents\loadlibrary-concurrent-work\exe-test.c @ 21] 28 | 07 000000aa`a0effcb0 00007ffa`56b5cc91 KERNEL32!BaseThreadInitThunk+0x14 29 | 08 000000aa`a0effce0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 30 | ``` 31 | 32 | 8. Continue execution until the return of `ntdll!LdrpProcessWork`: `gu` 33 | 9. [List all modules with their `DdagNode.State` values](/analysis-commands.mld##ldr_ddag_node-analysis) to verify work has been done 34 | 35 | ## Conclusion 36 | 37 | A concurrent `LoadLibrary` has successfully helped out another `LoadLibrary`. 38 | 39 | While in the `ntdll!LdrpDrainWorkQueue` function, the `LoadLibrary` on thread 2 thread will keep picking up work until there is none left in the `ntdll!LdrpWorkQueue`. Note that because thread 1 `LoadLibrary` never offloads (with the `ntdll!LdrpQueueWork` function) the work item for the top-level loading DLL (`shell32.dll` in this case) itelf, the program will freeze until thread 1 is resumed (`~m` command). The `LoadLibrary` on thread 2 will not start on any of its own mapping and snapping work (just on `dll-test.dll` in this case) while thread 1 is still undergoing its mapping and snapping work. 40 | 41 | When thread 2 `ntdll!LdrpDrainWorkQueue` sees that `ntdll!LdrpWorkInProgress` is already `1` and that there is no work left, it will try waiting on `ntdll!LdrpLoadCompleteEvent`. This will transition `ntdll!LdrpLoadCompleteEvent` to a waiting state and `ntdll!LdrpDrainQueue` will loop around in its inner `while` loop, if there is still no more work left then `ntdll!LdrpDrainWorkQueue` will end up waiting on `ntdll!LdrpLoadCompleteEvent`. 42 | 43 | The behavior of `ntdll!LdrpDrainWorkQueue` in regard to its waiting on `ntdll!LdrpLoadCompleteEvent` and when the loader sets `ntdll!LdrpLoadCompleteEvent` in the `ntdll!LdrpDropLastInProgressCount` function means module initialization cannot happen concurrently with module mapping and snapping. This method of working also extends to making a concurrent `LoadLibrary` unable to starts its own mapping and snapping work until a full load is complete. Although it would be desirable for performance and doable for module mapping and snapping to be entirely decoupled from module initialization, the current design of the loader does not allow for it. 44 | 45 | The `ntdll!LdrpQueueWork` function calls `ntdll!TpPostWork` (thread pool internals) to notify loader worker threads that there is work to pick up. A concurrent `LoadLibrary` never signs up for any such notifications, instead only helping out if work is immediately available for it to do, so on an ad-hoc basis. Loader worker threads access the `ntdll!LdrpWorkQueue` in the `ntdll!LdrpWorkCallback` function to get a work item (under the protection of `ntdll!LdrpWorkQueueLock`), then call `ntdll!LdrpProcessWork` on that work item to start processing it. 46 | 47 | **Final Result:** Yes, a concurrent `LoadLibrary` will pitch in to help with library mapping and snapping work, just as a loader worker thread would, if work is immediately available for it to do. 48 | -------------------------------------------------------------------------------- /code/windows/loadlibrary-concurrent-work/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | cl dll-test.c /DUNICODE /D_UNICODE /MD /LD /Zi /DEBUG /link /INCREMENTAL:NO 7 | cl exe-test.c /DUNICODE /D_UNICODE /MD /Zi /DEBUG /link /INCREMENTAL:NO 8 | 9 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 10 | if %ERRORLEVEL% equ 9009 ( 11 | echo Compiler not found. Opening Visual Studio developer environment... 12 | set "VSCMD_START_DIR=%cd%" 13 | set "VSCMD_SKIP_SENDTELEMETRY=1" 14 | rem Find the latest x64 Developer CMD version then open it 15 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 16 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 17 | ) 18 | -------------------------------------------------------------------------------- /code/windows/loadlibrary-concurrent-work/dll-test.c: -------------------------------------------------------------------------------- 1 | // Blank 2 | -------------------------------------------------------------------------------- /code/windows/loadlibrary-concurrent-work/exe-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | HANDLE start_library_loads; 5 | 6 | DWORD WINAPI loadlibrary_thread_1(LPVOID thread_started) { 7 | SetEvent(thread_started); 8 | WaitForSingleObject(start_library_loads, INFINITE); 9 | 10 | // Some library with lots of dependencies 11 | LoadLibrary(L"shell32.dll"); 12 | 13 | return 0; 14 | } 15 | 16 | DWORD WINAPI loadlibrary_thread_2(LPVOID thread_started) { 17 | SetEvent(thread_started); 18 | WaitForSingleObject(start_library_loads, INFINITE); 19 | 20 | Sleep(3000); 21 | LoadLibrary(L"dll-test.dll"); 22 | 23 | return 0; 24 | } 25 | 26 | #define NUM_THREADS 2 27 | 28 | int main() { 29 | // Create event for starting library loads 30 | start_library_loads = CreateEvent(NULL, TRUE, FALSE, NULL); 31 | if (!start_library_loads) { 32 | printf("Failed to create event: %lu\n", GetLastError()); 33 | return EXIT_FAILURE; 34 | } 35 | 36 | // Create an event for signalling when each thread has started 37 | HANDLE thread_started_events[NUM_THREADS]; 38 | for (int i = 0; i < NUM_THREADS; ++i) { 39 | thread_started_events[i] = CreateEvent(NULL, TRUE, FALSE, NULL); 40 | if (!thread_started_events[i]) { 41 | printf("Failed to create event: %lu\n", GetLastError()); 42 | return EXIT_FAILURE; 43 | } 44 | } 45 | 46 | // Create threads 47 | HANDLE threads[NUM_THREADS]; 48 | PVOID routines[NUM_THREADS] = { loadlibrary_thread_1, loadlibrary_thread_2 }; 49 | for (int i = 0; i < NUM_THREADS; ++i) { 50 | threads[i] = CreateThread(NULL, 0, routines[i], thread_started_events[i], 0, NULL); 51 | if (!threads[i]) { 52 | printf("Failed to create thread: %lu\n", GetLastError()); 53 | return EXIT_FAILURE; 54 | } 55 | } 56 | 57 | // Wait for all threads to start 58 | DWORD result = WaitForMultipleObjects(NUM_THREADS, thread_started_events, TRUE, INFINITE); 59 | 60 | // Run any debugger commands for experimenting here 61 | __debugbreak(); 62 | 63 | // Let the LoadLibrary threads run loose! 64 | if (result == WAIT_OBJECT_0) 65 | SetEvent(start_library_loads); 66 | 67 | // Join threads before application exits 68 | result = WaitForMultipleObjects(NUM_THREADS, threads, TRUE, INFINITE); 69 | } 70 | 71 | -------------------------------------------------------------------------------- /code/windows/loadlibrary-init-circular-dependency/README.md: -------------------------------------------------------------------------------- 1 | # `LoadLibrary` Initialization Circular Dependency Experiment 2 | 3 | We evaluate the loader's behavior when encountering a circular dependency during module initialization. 4 | 5 | See the [glibc equivalent](/code/glibc/dlopen-init-interruption/README.md) experiment. 6 | 7 | ## Result 8 | 9 | ``` 10 | Start: DLL 1 Init 11 | Start: DLL 2 Init 12 | DLL 1, func 13 | End: DLL 2 Init 14 | End: DLL 1 Init 15 | ``` 16 | 17 | **Conclusion:** As expected, the Windows loader has no choice but to initialize DLL 2 while DLL 1 could be partially initialized due to a circular dependency. The PE executable format exposes all initialization and finalization tasks as merged into one externally callable `EntryPoint` and arbitrary code cannot be interrupted or preempted, so the Windows loader's current behavior is the best it can be under the conditions. 18 | -------------------------------------------------------------------------------- /code/windows/loadlibrary-init-circular-dependency/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | cl lib1.c /DUNICODE /D_UNICODE /MD /LD /Zi /DEBUG /link /INCREMENTAL:NO 7 | rem Intentionally give lib2 a circular dependency because lib1 also dynamically loads lib2 8 | cl lib2.c lib1.lib /DUNICODE /D_UNICODE /MD /LD /Zi /DEBUG /link /INCREMENTAL:NO 9 | cl exe-test.c lib1.lib /DUNICODE /D_UNICODE /MD /Zi /DEBUG /link /INCREMENTAL:NO 10 | 11 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 12 | if %ERRORLEVEL% equ 9009 ( 13 | echo Compiler not found. Opening Visual Studio developer environment... 14 | set "VSCMD_START_DIR=%cd%" 15 | set "VSCMD_SKIP_SENDTELEMETRY=1" 16 | rem Find the latest x64 Developer CMD version then open it 17 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 18 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 19 | ) 20 | -------------------------------------------------------------------------------- /code/windows/loadlibrary-init-circular-dependency/exe-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Dynamically link to test DLL 4 | __declspec(dllimport) void DummyExport(); 5 | 6 | int main() { 7 | // Call dummy export so our link to the DLL isn't optimized out 8 | DummyExport(); 9 | 10 | return EXIT_SUCCESS; 11 | } 12 | -------------------------------------------------------------------------------- /code/windows/loadlibrary-init-circular-dependency/lib1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | EXTERN_C __declspec(dllexport) void func() { 5 | puts("DLL 1, func"); 6 | } 7 | 8 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { 9 | switch (fdwReason) { 10 | case DLL_PROCESS_ATTACH: 11 | puts("Start: DLL 1 Init"); 12 | 13 | HMODULE lib2 = LoadLibrary(L"lib2.dll"); 14 | if (!lib2) { 15 | __debugbreak(); 16 | } 17 | 18 | puts("End: DLL 1 Init"); 19 | break; 20 | } 21 | 22 | return TRUE; 23 | } 24 | 25 | EXTERN_C __declspec(dllexport) void DummyExport() { 26 | // Exported function that does nothing so we can dynamically link 27 | //puts("Test export"); 28 | } 29 | -------------------------------------------------------------------------------- /code/windows/loadlibrary-init-circular-dependency/lib2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Dynamically link to func from lib1 5 | __declspec(dllimport) void func(); 6 | 7 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { 8 | switch (fdwReason) { 9 | case DLL_PROCESS_ATTACH: 10 | puts("Start: DLL 2 Init"); 11 | 12 | // Creating the circular dependency via dynamic linking or dynamic loading (or doubling down by doing both) gives the same result, which essentially means this LoadLibrary is a no-op because library 1 is already in its initialization phase (and code cannot arbitrarily be interrupted or preempted) 13 | //HMODULE lib1 = LoadLibrary(L"lib1.dll"); 14 | //if (!lib1) { 15 | // __debugbreak(); 16 | //} 17 | 18 | func(); 19 | 20 | puts("End: DLL 2 Init"); 21 | break; 22 | } 23 | 24 | return TRUE; 25 | } 26 | -------------------------------------------------------------------------------- /code/windows/loadlibrary/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | cl dll-test.c /DUNICODE /D_UNICODE /MD /LD /Zi /DEBUG /link /INCREMENTAL:NO 7 | cl exe-test.c /DUNICODE /D_UNICODE /MD /Zi /DEBUG /link /INCREMENTAL:NO 8 | 9 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 10 | if %ERRORLEVEL% equ 9009 ( 11 | echo Compiler not found. Opening Visual Studio developer environment... 12 | set "VSCMD_START_DIR=%cd%" 13 | set "VSCMD_SKIP_SENDTELEMETRY=1" 14 | rem Find the latest x64 Developer CMD version then open it 15 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 16 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 17 | ) 18 | -------------------------------------------------------------------------------- /code/windows/loadlibrary/dll-test.c: -------------------------------------------------------------------------------- 1 | // Blank 2 | -------------------------------------------------------------------------------- /code/windows/loadlibrary/exe-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | LoadLibrary(L"dll-test.dll"); 5 | } -------------------------------------------------------------------------------- /code/windows/ntterminateprocess-test-harness/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | cl ntterminateprocess-test-harness.c /DUNICODE /D_UNICODE /MD /Zi /DEBUG /link ntdll.lib shell32.lib /INCREMENTAL:NO 7 | 8 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 9 | if %ERRORLEVEL% equ 9009 ( 10 | echo Compiler not found. Opening Visual Studio developer environment... 11 | set "VSCMD_START_DIR=%cd%" 12 | set "VSCMD_SKIP_SENDTELEMETRY=1" 13 | rem Find the latest x64 Developer CMD version then open it 14 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 15 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 16 | ) 17 | -------------------------------------------------------------------------------- /code/windows/thread-performance/native-thread-fls-alloc/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | rem Exclude debug flags for performance test 7 | cl dll-test.c /DUNICODE /D_UNICODE /MD /LD /link /INCREMENTAL:NO 8 | cl thread-test.c dll-test.lib /DUNICODE /D_UNICODE /MD /link /INCREMENTAL:NO 9 | 10 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 11 | if %ERRORLEVEL% equ 9009 ( 12 | echo Compiler not found. Opening Visual Studio developer environment... 13 | set "VSCMD_START_DIR=%cd%" 14 | set "VSCMD_SKIP_SENDTELEMETRY=1" 15 | rem Find the latest x64 Developer CMD version then open it 16 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 17 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 18 | ) 19 | -------------------------------------------------------------------------------- /code/windows/thread-performance/native-thread-fls-alloc/dll-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | EXTERN_C __declspec(dllexport) void DummyExport() { 5 | // Exported function that does nothing so we can dynamically link 6 | puts("Test export"); 7 | } 8 | 9 | #define NUM_FLS 50 10 | 11 | void WINAPI flsCallback(PVOID flsData) { 12 | // Typical FLS usage pattern 13 | if (!HeapFree(GetProcessHeap(), 0, flsData)) { 14 | __debugbreak(); 15 | } 16 | //puts("flsCallback"); 17 | } 18 | 19 | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { 20 | switch (fdwReason) { 21 | case DLL_THREAD_ATTACH: 22 | for (int i = 0; i < NUM_FLS; ++i) { 23 | LPVOID alloc = HeapAlloc(GetProcessHeap(), 0, 16); 24 | if (!alloc) { 25 | __debugbreak(); 26 | } 27 | // Typical FLS usage pattern: https://learn.microsoft.com/en-us/windows/win32/api/fibersapi/nf-fibersapi-flsalloc#remarks 28 | DWORD flsIdx = FlsAlloc(&flsCallback); 29 | FlsSetValue(flsIdx, alloc); 30 | } 31 | break; 32 | } 33 | 34 | return TRUE; 35 | } 36 | -------------------------------------------------------------------------------- /code/windows/thread-performance/native-thread-fls-alloc/thread-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // Dynamically link to DLL 5 | __declspec(dllimport) void DummyExport(); 6 | 7 | #define NUM_THREADS 10000 8 | 9 | DWORD WINAPI dummy_thread(LPVOID lpParam) { 10 | // Thread does nothing and exits 11 | return 0; 12 | } 13 | 14 | int main() { 15 | // Call dummy export so our link to the DLL isn't optimized out 16 | DummyExport(); 17 | 18 | HANDLE threads[NUM_THREADS]; 19 | printf("Creating %d threads...\n", NUM_THREADS); 20 | 21 | int thread_count = 0; 22 | 23 | LARGE_INTEGER start, end, frequency; 24 | QueryPerformanceFrequency(&frequency); 25 | QueryPerformanceCounter(&start); 26 | 27 | while (thread_count < NUM_THREADS) { 28 | threads[thread_count] = CreateThread(NULL, 0, dummy_thread, NULL, 0, NULL); 29 | if (threads[thread_count] == NULL) { 30 | break; 31 | } 32 | 33 | ++thread_count; 34 | } 35 | 36 | for (int i = 0; i < thread_count; ++i) { 37 | WaitForSingleObject(threads[i], INFINITE); 38 | CloseHandle(threads[i]); 39 | } 40 | 41 | QueryPerformanceCounter(&end); 42 | 43 | if (thread_count < NUM_THREADS) { 44 | fprintf(stderr, "Error: Unable to create thread %d\n", thread_count + 1); 45 | return EXIT_FAILURE; 46 | } 47 | 48 | double elapsed = (double)(end.QuadPart - start.QuadPart) / frequency.QuadPart; 49 | printf("Time taken to create and join %d threads (seconds): %.2f\n", NUM_THREADS, elapsed); 50 | 51 | return EXIT_SUCCESS; 52 | } 53 | -------------------------------------------------------------------------------- /code/windows/thread-performance/native-thread-no-loader-init/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | rem Exclude debug flags for performance test 7 | cl thread-test.c /DUNICODE /D_UNICODE /MD /link ntdll.lib /INCREMENTAL:NO 8 | 9 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 10 | if %ERRORLEVEL% equ 9009 ( 11 | echo Compiler not found. Opening Visual Studio developer environment... 12 | set "VSCMD_START_DIR=%cd%" 13 | set "VSCMD_SKIP_SENDTELEMETRY=1" 14 | rem Find the latest x64 Developer CMD version then open it 15 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 16 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 17 | ) 18 | -------------------------------------------------------------------------------- /code/windows/thread-performance/native-thread-no-loader-init/thread-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Define the flag for skipping thread loader initialization/deinitialization (DLL_THREAD_ATTACH/DLL_THREAD_DETACH) 6 | // https://ntdoc.m417z.com/thread_create_flags_skip_loader_init 7 | #define THREAD_CREATE_FLAGS_SKIP_LOADER_INIT 0x20 8 | 9 | #define NUM_THREADS 10000 10 | 11 | DWORD WINAPI dummy_thread(LPVOID lpParam) { 12 | // Thread does nothing and exits 13 | return 0; 14 | } 15 | 16 | int main() { 17 | HANDLE threads[NUM_THREADS]; 18 | printf("Creating %d threads...\n", NUM_THREADS); 19 | 20 | int thread_count = 0; 21 | 22 | LARGE_INTEGER start, end, frequency; 23 | QueryPerformanceFrequency(&frequency); 24 | QueryPerformanceCounter(&start); 25 | 26 | while (thread_count < NUM_THREADS) { 27 | NTSTATUS status = NtCreateThreadEx( 28 | &threads[thread_count], // Thread handle 29 | THREAD_ALL_ACCESS, // Access rights 30 | NULL, // Object attributes 31 | GetCurrentProcess(), // Process handle 32 | dummy_thread, // Thread function 33 | NULL, // Argument 34 | THREAD_CREATE_FLAGS_SKIP_LOADER_INIT, // Flags: Skip loader initialization 35 | 0, // Zero bits 36 | 0, // Stack size 37 | 0, // Maximum stack size 38 | NULL // Attribute list 39 | ); 40 | if (!NT_SUCCESS(status)) { 41 | break; 42 | } 43 | 44 | ++thread_count; 45 | } 46 | 47 | for (int i = 0; i < thread_count; ++i) { 48 | WaitForSingleObject(threads[i], INFINITE); 49 | CloseHandle(threads[i]); 50 | } 51 | 52 | QueryPerformanceCounter(&end); 53 | 54 | if (thread_count < NUM_THREADS) { 55 | fprintf(stderr, "Error: Unable to create thread %d\n", thread_count + 1); 56 | return EXIT_FAILURE; 57 | } 58 | 59 | double elapsed = (double)(end.QuadPart - start.QuadPart) / frequency.QuadPart; 60 | printf("Time taken to create and join %d threads (seconds): %.2f\n", NUM_THREADS, elapsed); 61 | 62 | return EXIT_SUCCESS; 63 | } 64 | -------------------------------------------------------------------------------- /code/windows/thread-performance/native-thread/build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem These options replicate Visual Studio 4 | rem /MD: Use UCRT instead of statically linking CRT into modules 5 | rem /INCREMENTAL:NO: Remove "ILT" from symbol names 6 | rem Exclude debug flags for performance test 7 | cl thread-test.c /DUNICODE /D_UNICODE /MD /link /INCREMENTAL:NO 8 | 9 | rem Helper: If compilation fails because cl command doesn't exist then re-run in the correct environment 10 | if %ERRORLEVEL% equ 9009 ( 11 | echo Compiler not found. Opening Visual Studio developer environment... 12 | set "VSCMD_START_DIR=%cd%" 13 | set "VSCMD_SKIP_SENDTELEMETRY=1" 14 | rem Find the latest x64 Developer CMD version then open it 15 | rem ^&: Inject running our build script after Visual Studio script (bug -> feature) 16 | for /f "delims=" %%f in ('dir /b /s /A-D /O-N "%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\x64 Native Tools Command Prompt for VS *.lnk"') do start "" "%%f" ^& build.bat & goto :EOF 17 | ) 18 | -------------------------------------------------------------------------------- /code/windows/thread-performance/native-thread/thread-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define NUM_THREADS 10000 5 | 6 | DWORD WINAPI dummy_thread(LPVOID lpParam) { 7 | // Thread does nothing and exits 8 | return 0; 9 | } 10 | 11 | int main() { 12 | HANDLE threads[NUM_THREADS]; 13 | printf("Creating %d threads...\n", NUM_THREADS); 14 | 15 | int thread_count = 0; 16 | 17 | LARGE_INTEGER start, end, frequency; 18 | QueryPerformanceFrequency(&frequency); 19 | QueryPerformanceCounter(&start); 20 | 21 | while (thread_count < NUM_THREADS) { 22 | threads[thread_count] = CreateThread(NULL, 0, dummy_thread, NULL, 0, NULL); 23 | if (threads[thread_count] == NULL) { 24 | break; 25 | } 26 | 27 | ++thread_count; 28 | } 29 | 30 | for (int i = 0; i < thread_count; ++i) { 31 | WaitForSingleObject(threads[i], INFINITE); 32 | CloseHandle(threads[i]); 33 | } 34 | 35 | QueryPerformanceCounter(&end); 36 | 37 | if (thread_count < NUM_THREADS) { 38 | fprintf(stderr, "Error: Unable to create thread %d\n", thread_count + 1); 39 | return EXIT_FAILURE; 40 | } 41 | 42 | double elapsed = (double)(end.QuadPart - start.QuadPart) / frequency.QuadPart; 43 | printf("Time taken to create and join %d threads (seconds): %.2f\n", NUM_THREADS, elapsed); 44 | 45 | return EXIT_SUCCESS; 46 | } 47 | -------------------------------------------------------------------------------- /data/windows/LdrpDrainWorkQueue-decompiled.txt: -------------------------------------------------------------------------------- 1 | // This is the original decompilation of the LdrpDrainWorkQueue function I reverse engineered 2 | 3 | struct _TEB *__fastcall LdrpDrainWorkQueue(int a1) 4 | { 5 | HANDLE v1; // r14 6 | char v2; // si 7 | char v4; // bp 8 | __int64 *v5; // rbx 9 | __int64 v6; // rax 10 | __int64 v7; // rdx 11 | struct _TEB *result; // rax 12 | __int64 v9; // rax 13 | __int64 v10; // rax 14 | 15 | v1 = (HANDLE)LdrpWorkCompleteEvent; 16 | v2 = 0; 17 | if ( !a1 ) 18 | v1 = LdrpLoadCompleteEvent; 19 | while ( 1 ) 20 | { 21 | while ( 1 ) 22 | { 23 | RtlEnterCriticalSection(&LdrpWorkQueueLock); 24 | v4 = LdrpDetourExist; 25 | if ( !LdrpDetourExist || a1 == 1 ) 26 | { 27 | v5 = (__int64 *)LdrpWorkQueue; 28 | if ( *(__int64 **)(LdrpWorkQueue + 8) != &LdrpWorkQueue 29 | || (v6 = *(_QWORD *)LdrpWorkQueue, *(_QWORD *)(*(_QWORD *)LdrpWorkQueue + 8i64) != LdrpWorkQueue) ) 30 | { 31 | __fastfail(3u); 32 | } 33 | LdrpWorkQueue = *(_QWORD *)LdrpWorkQueue; 34 | *(_QWORD *)(v6 + 8) = &LdrpWorkQueue; 35 | if ( &LdrpWorkQueue == v5 ) 36 | { 37 | if ( LdrpWorkInProgress == a1 ) 38 | { 39 | LdrpWorkInProgress = 1; 40 | v2 = 1; 41 | } 42 | } 43 | else 44 | { 45 | if ( !v4 ) 46 | ++LdrpWorkInProgress; 47 | LdrpUpdateStatistics(); 48 | } 49 | } 50 | else 51 | { 52 | if ( LdrpWorkInProgress == a1 ) 53 | { 54 | LdrpWorkInProgress = 1; 55 | v2 = 1; 56 | } 57 | v5 = &LdrpWorkQueue; 58 | } 59 | RtlLeaveCriticalSection(&LdrpWorkQueueLock); 60 | if ( v2 ) 61 | break; 62 | if ( &LdrpWorkQueue == v5 ) 63 | { 64 | NtWaitForSingleObject(v1, 0, 0i64); 65 | } 66 | else 67 | { 68 | LOBYTE(v7) = v4; 69 | LdrpProcessWork(v5 - 8, v7); 70 | } 71 | } 72 | if ( !a1 || (__int64 *)LdrpRetryQueue == &LdrpRetryQueue ) 73 | break; 74 | RtlEnterCriticalSection(&LdrpWorkQueueLock); 75 | v9 = LdrpRetryQueue; 76 | *(_QWORD *)(LdrpRetryQueue + 8) = &LdrpWorkQueue; 77 | LdrpWorkQueue = v9; 78 | v10 = qword_18016C3A8; 79 | *(_QWORD *)qword_18016C3A8 = &LdrpWorkQueue; 80 | qword_18016C3F8 = v10; 81 | qword_18016C3A8 = (__int64)&LdrpRetryQueue; 82 | LdrpRetryQueue = (__int64)&LdrpRetryQueue; 83 | LdrpRetryingModuleIndex = 0i64; 84 | RtlLeaveCriticalSection(&LdrpWorkQueueLock); 85 | v2 = 0; 86 | } 87 | result = NtCurrentTeb(); 88 | result->SameTebFlags |= 0x1000u; 89 | return result; 90 | } 91 | -------------------------------------------------------------------------------- /data/windows/RtlpWaitOnCriticalSection-NtTerminateProcess-call-stack-sample.txt: -------------------------------------------------------------------------------- 1 | Abandoned critical section: KERNELBASE!wil::details::g_featureStateManager+b0 2 | 3 | 0:000> k 4 | # Child-SP RetAddr Call Site 5 | 00 0000001c`5f0ff468 00007ff9`db3b3671 ntdll!NtTerminateProcess+0x14 6 | 01 0000001c`5f0ff470 00007ff9`db37fcb4 ntdll!RtlpWaitOnCriticalSection+0x221 7 | 02 0000001c`5f0ff550 00007ff9`db37fae2 ntdll!RtlpEnterCriticalSectionContended+0x1c4 8 | 03 0000001c`5f0ff5b0 00007ff9`d8ddbec8 ntdll!RtlEnterCriticalSection+0x42 9 | 04 0000001c`5f0ff5e0 00007ff9`d8ddc1dc KERNELBASE!wil::details_abi::SubscriptionList::Unsubscribe+0x1c 10 | 05 0000001c`5f0ff630 00007ff9`d8ddc889 KERNELBASE!wil::details::WilApiImpl_UnsubscribeFeatureStateChangeNotification+0x4c 11 | 06 0000001c`5f0ff660 00007ff9`d8d570f2 KERNELBASE!wil_UninitializeFeatureStagingUsageReporting+0x15 12 | 07 0000001c`5f0ff690 00007ff9`d8d56879 KERNELBASE!WilInitialize+0x102 13 | 08 0000001c`5f0ff6c0 00007ff9`db369a1d KERNELBASE!KernelBaseDllInitialize+0xc9 14 | 09 0000001c`5f0ff6f0 00007ff9`db3adcda ntdll!LdrpCallInitRoutine+0x61 15 | 0a 0000001c`5f0ff760 00007ff9`db3ada8d ntdll!LdrShutdownProcess+0x22a 16 | 0b 0000001c`5f0ff870 00007ff9`daabe3bb ntdll!RtlExitUserProcess+0xad 17 | 0c 0000001c`5f0ff8a0 00007ff9`d8c605bc KERNEL32!ExitProcessImplementation+0xb 18 | 0d 0000001c`5f0ff8d0 00007ff9`d8c6045f ucrtbase!exit_or_terminate_process+0x44 19 | 0e 0000001c`5f0ff900 00007ff6`ed6d12a7 ucrtbase!common_exit+0x6f 20 | 0f 0000001c`5f0ff950 00007ff9`daab7344 ConsoleApplication2!__scrt_common_main_seh+0x173 [d:\a01\_work\20\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 295] 21 | 10 0000001c`5f0ff990 00007ff9`db3a26b1 KERNEL32!BaseThreadInitThunk+0x14 22 | 11 0000001c`5f0ff9c0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 23 | -------------------------------------------------------------------------------- /data/windows/com-components/get-com-components.ps1: -------------------------------------------------------------------------------- 1 | # Get registered COM components (HKCR to include system-wide and per-user) 2 | # Each COM class is a component 3 | # 4 | # PowerShell sucks, so clean up the output before commiting to Git repo: 5 | # 1. dos2unix com-components.txt 6 | # - UTF-16LE with BOM -> UTF-8 and CRLF -> LF 7 | # 2. sed -i 's/[[:space:]]*$//' com-components.txt 8 | # - Remove trailing spaces before each end of line 9 | # - Believe it or not, PowerShell includes a box of whitespace the size of your PowerShell window in the output. As a result, file size grows in large linear increments based on window size. 10 | # 11 | # Also, make sure your window is full sized when you run the command so entires aren't split between lines even though output is to a file (apparently isatty() doesn't exist on Windows?) 12 | Get-ChildItem Registry::HKEY_CLASSES_ROOT\CLSID | Get-ItemProperty | Select-Object PsChildName, "(default)" | Out-File -FilePath "com-components.txt" -NoNewline 13 | -------------------------------------------------------------------------------- /data/windows/dll-best-practices/DLL_bestprac.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ElliotKillick/operating-system-design-review/c3193467616797ead839cb34e2f43d68473639d2/data/windows/dll-best-practices/DLL_bestprac.doc -------------------------------------------------------------------------------- /data/windows/dll-best-practices/README.md: -------------------------------------------------------------------------------- 1 | # DLL Best Practices 2 | 3 | The "DLL Best Practices" whitepaper is the Microsoft Corporation's original guidance laying out DLL best practices for the Windows platform, namely regarding `DllMain`. 4 | 5 | The document was authored by engineers who were assigned to working on the DLL loader at Microsoft. The final document was published in cooperation with the [product manager (PM)](https://medium.com/@adiagashe/microsoft-program-manager-overview-everything-you-need-to-know-from-application-to-interview-33eab7fb0dde) [who owned the DLL loader](https://learn.microsoft.com/en-us/archive/blogs/larryosterman/best-practices-for-dllmain) ([blog announcement #2](https://learn.microsoft.com/en-us/archive/blogs/matt_pietrek/finally-some-more-concrete-guidance-on-the-mysterys-of-dllmain)). 6 | 7 | In my opinion, the document was good for its time, except for the main "General Best Practices" section, which throws rules at you without any structure or reasoning. People reading the old `DllMain` "General Best Practices" today (on Microsoft's documentation website, which has undergone a few updates) tend to come out more confused about `DllMain` safety than they were going in. So, let's rewrite it with the perspective of an ecosystem where `DllMain` (module initializer/finalizer or constructor/destructor in the module scope) problems don't happen to begin with. **Enter... Unix-like operating systems!** 8 | 9 | Something worth pointing out is that Old New Thing author Raymond Chen [didn't condone referencing his work in the official "DLL Best Practices" whitepaper](https://devblogs.microsoft.com/oldnewthing/20070810-00/?p=25623). Many years have passed since this original statement; however, his extensive Windows experience inside Microsoft makes it nearly impossible not to reference his abundance of work in an educational manner, especially for someone outside of Microsoft who, in the grand scheme of Windows, knows little to nothing about Windows internals like myself (not that anyone could have a firm grasp on the whole of a project that's as large as Windows, but you get what I mean). 10 | 11 | Be mindful of the fact that my critiques apply only to the software or company itself and not to the people who contribute to its development. I appreciate and respect the people who put hard work into creating the technologies. Thank you! 12 | 13 | **Note:** This old document states the following about loader lock: "Any function that must read or modify the per-process library-loader data structures must acquire this lock before performing such an operation" (in addition to protecting module initialization/deinitialization). This fact was only true of the legacy Windows loader (e.g. see ReactOS code based on the reverse engineered Windows Server 2003 loader). The modern Windows loader protects library-loader data structures using the `LdrpModuleDatatableLock` lock. The age of this document also makes it inapplicable to the modern Windows loader in other ways (this amendment is by no means comprehensive). 14 | 15 | [Current official Microsoft document download](https://download.microsoft.com/download/a/f/7/af7777e5-7dcd-4800-8a0a-b18336565f5b/DLL_bestprac.doc) | [Current official Microsoft document web view](https://view.officeapps.live.com/op/view.aspx?src=http%3A%2F%2Fdownload.microsoft.com%2Fdownload%2Fa%2Ff%2F7%2Faf7777e5-7dcd-4800-8a0a-b18336565f5b%2FDLL_bestprac.doc) | [Legacy download page archived](https://web.archive.org/web/20101029013644/http://www.microsoft.com/whdc/driver/kernel/DLL_bestprac.mspx) 16 | -------------------------------------------------------------------------------- /data/windows/dll-deps-research/dlls-depending-on-service-dlls.txt: -------------------------------------------------------------------------------- 1 | Service DLL | DLL taking dependency 2 | appinfo.dll: appinfoext.dll 3 | APPMGMTS.dll: appmgr.dll 4 | AppxDeploymentServer.dll: AppXDeploymentExtensions.desktop.dll 5 | AppxDeploymentServer.dll: AppXDeploymentExtensions.onecore.dll 6 | CoreMessaging.dll: ActivationManager.dll 7 | CoreMessaging.dll: Analog.Shell.Broker.dll 8 | CoreMessaging.dll: CBDHSvc.dll 9 | CoreMessaging.dll: CoreShell.dll 10 | CoreMessaging.dll: CoreUIComponents.dll 11 | CoreMessaging.dll: dcomp.dll 12 | CoreMessaging.dll: DesktopView.Internal.Broker.dll 13 | CoreMessaging.dll: DictationManager.dll 14 | CoreMessaging.dll: dwmcore.dll 15 | CoreMessaging.dll: dwmredir.dll 16 | CoreMessaging.dll: edgehtml.dll 17 | CoreMessaging.dll: EditBufferTestHook.dll 18 | CoreMessaging.dll: ExecModelClient.dll 19 | CoreMessaging.dll: GraphicsCapture.dll 20 | CoreMessaging.dll: HeatCore.dll 21 | CoreMessaging.dll: HologramCompositor.dll 22 | CoreMessaging.dll: HologramWorld.dll 23 | CoreMessaging.dll: HolographicExtensions.dll 24 | CoreMessaging.dll: HoloShellRuntime.dll 25 | CoreMessaging.dll: HoloSI.PCShell.dll 26 | CoreMessaging.dll: Hydrogen.dll 27 | CoreMessaging.dll: InputHost.dll 28 | CoreMessaging.dll: InputLocaleManager.dll 29 | CoreMessaging.dll: InputService.dll 30 | CoreMessaging.dll: ISM.dll 31 | CoreMessaging.dll: MixedReality.Broker.dll 32 | CoreMessaging.dll: modernexecserver.dll 33 | CoreMessaging.dll: msctf.dll 34 | CoreMessaging.dll: MsSpellCheckingFacility.dll 35 | CoreMessaging.dll: MTF.dll 36 | CoreMessaging.dll: MTFAppServiceDS.dll 37 | CoreMessaging.dll: MTFServer.dll 38 | CoreMessaging.dll: navshutdown.dll 39 | CoreMessaging.dll: SettingsHandlers_AnalogShell.dll 40 | CoreMessaging.dll: SettingsHandlers_Language.dll 41 | CoreMessaging.dll: SettingsHandlers_Region.dll 42 | CoreMessaging.dll: SettingSync.dll 43 | CoreMessaging.dll: ShareHost.dll 44 | CoreMessaging.dll: SRH.dll 45 | CoreMessaging.dll: StartTileData.dll 46 | CoreMessaging.dll: TextInputFramework.dll 47 | CoreMessaging.dll: TextInputMethodFormatter.dll 48 | CoreMessaging.dll: tsf3gip.dll 49 | CoreMessaging.dll: twinapi.appcore.dll 50 | CoreMessaging.dll: twinui.pcshell.dll 51 | CoreMessaging.dll: UiaManager.dll 52 | CoreMessaging.dll: UIAutomationCore.dll 53 | CoreMessaging.dll: WindowManagement.dll 54 | CoreMessaging.dll: WindowManagementAPI.dll 55 | CoreMessaging.dll: windows.applicationmodel.datatransfer.dll 56 | CoreMessaging.dll: Windows.Mirage.dll 57 | CoreMessaging.dll: Windows.UI.Core.TextInput.dll 58 | CoreMessaging.dll: Windows.UI.dll 59 | CoreMessaging.dll: Windows.UI.Input.Inking.dll 60 | CoreMessaging.dll: Windows.UI.Logon.dll 61 | CoreMessaging.dll: Windows.UI.Xaml.dll 62 | CoreMessaging.dll: Windows.UI.XamlHost.dll 63 | CoreMessaging.dll: windowsudk.shellcommon.dll 64 | deviceaccess.dll: ConsentUX.dll 65 | GPSVC.dll: fdeploy.dll 66 | ISCSIEXE.dll: iscsied.dll 67 | NETLOGON.dll: kerberos.dll 68 | NETLOGON.dll: laps.dll 69 | NETLOGON.dll: msv1_0.dll 70 | NETLOGON.dll: samsrv.dll 71 | PLA.dll: wdc.dll 72 | umpo.dll: umpoext.dll 73 | PROFSVC.dll: profsvcext.dll 74 | qwave.dll: MiracastReceiver.dll 75 | qwave.dll: Windows.Media.Streaming.dll 76 | qwave.dll: wmp.dll 77 | rasmans.dll: rascustom.dll 78 | rasmans.dll: vpnike.dll 79 | Windows.StateRepository.dll: TileDataRepository.dll 80 | Windows.StateRepository.dll: Windows.StateRepositoryUpgrade.dll 81 | wiaservc.dll: EsclWiaDriver.dll 82 | wiaservc.dll: PortableDeviceWiaCompat.dll 83 | tzautoupdate.dll: CloudExperienceHostCommon.dll 84 | tzautoupdate.dll: Windows.System.SystemManagement.dll 85 | unistore.dll: ActiveSyncProvider.dll 86 | unistore.dll: cemapi.dll 87 | unistore.dll: PhoneService.dll 88 | unistore.dll: PimIndexMaintenance.dll 89 | unistore.dll: PimIndexMaintenanceClient.dll 90 | unistore.dll: Pimstore.dll 91 | unistore.dll: POSyncServices.dll 92 | unistore.dll: UserDataService.dll 93 | wdi.dll: Apphlpdm.dll 94 | wdi.dll: cofiredm.dll 95 | wdi.dll: dfdts.dll 96 | wdi.dll: diagperf.dll 97 | wdi.dll: dps.dll 98 | WDI.dll: dxgwdi.dll 99 | wdi.dll: fthsvc.dll 100 | wdi.dll: MsiCofire.dll 101 | wdi.dll: ndfapi.dll 102 | wdi.dll: netdiagfx.dll 103 | wdi.dll: pcadm.dll 104 | wdi.dll: perftrack.dll 105 | wdi.dll: pnpts.dll 106 | wdi.dll: pots.dll 107 | wdi.dll: radardt.dll 108 | wdi.dll: radarrs.dll 109 | wdi.dll: whealogr.dll 110 | wdi.dll: Apphlpdm.dll 111 | wdi.dll: cofiredm.dll 112 | wdi.dll: dfdts.dll 113 | wdi.dll: diagperf.dll 114 | wdi.dll: dps.dll 115 | WDI.dll: dxgwdi.dll 116 | wdi.dll: fthsvc.dll 117 | wdi.dll: MsiCofire.dll 118 | wdi.dll: ndfapi.dll 119 | wdi.dll: netdiagfx.dll 120 | wdi.dll: pcadm.dll 121 | wdi.dll: perftrack.dll 122 | wdi.dll: pnpts.dll 123 | wdi.dll: pots.dll 124 | wdi.dll: radardt.dll 125 | wdi.dll: radarrs.dll 126 | wdi.dll: whealogr.dll 127 | wercplsupport.dll: werconcpl.dll 128 | WINHTTP.dll: aadauthhelper.dll 129 | WINHTTP.dll: aadcloudap.dll 130 | WINHTTP.dll: ActiveSyncProvider.dll 131 | WINHTTP.dll: aemarebackup.dll 132 | WINHTTP.dll: APMon.dll 133 | WINHTTP.dll: appraiser.dll 134 | WINHTTP.dll: AppVPublishing.dll 135 | WINHTTP.dll: AppVReporting.dll 136 | WINHTTP.dll: AuthBroker.dll 137 | WINHTTP.dll: autopilot.dll 138 | WINHTTP.dll: AxInstSv.dll 139 | WINHTTP.dll: AzureSettingSyncProvider.dll 140 | WINHTTP.dll: BingASDS.dll 141 | WINHTTP.dll: BingOnlineServices.dll 142 | WINHTTP.dll: bitsigd.dll 143 | WINHTTP.dll: cdosys.dll 144 | WINHTTP.dll: cdp.dll 145 | WINHTTP.dll: certcli.dll 146 | WINHTTP.dll: CloudExperienceHostCommon.dll 147 | WINHTTP.dll: cloudidsvc.dll 148 | WINHTTP.dll: cryptnet.dll 149 | WINHTTP.dll: cryptsvc.dll 150 | WINHTTP.dll: CspProxy.dll 151 | WINHTTP.dll: DAFIPP.dll 152 | WINHTTP.dll: DAFMCP.dll 153 | WINHTTP.dll: dafupnp.dll 154 | WINHTTP.dll: DAFWiProv.dll 155 | WINHTTP.dll: DAFWSD.dll 156 | WINHTTP.dll: DaOtpCredentialProvider.dll 157 | WINHTTP.dll: davclnt.dll 158 | WINHTTP.dll: DavSyncProvider.dll 159 | WINHTTP.dll: dcntel.dll 160 | WINHTTP.dll: DeviceMetadataRetrievalClient.dll 161 | WINHTTP.dll: diagtrack.dll 162 | WINHTTP.dll: dmcmnutils.dll 163 | WINHTTP.dll: dmcsps.dll 164 | WINHTTP.dll: dmenrollengine.dll 165 | WINHTTP.dll: domgmt.dll 166 | WINHTTP.dll: dosvc.dll 167 | WINHTTP.dll: dot3mm.dll 168 | WINHTTP.dll: dsreg.dll 169 | WINHTTP.dll: edgehtml.dll 170 | WINHTTP.dll: edputil.dll 171 | WINHTTP.dll: efscore.dll 172 | WINHTTP.dll: EnterpriseAppMgmtSvc.dll 173 | WINHTTP.dll: EsclProtocol.dll 174 | WINHTTP.dll: EsclScan.dll 175 | WINHTTP.dll: EthernetMediaManager.dll 176 | WINHTTP.dll: facecredentialprovider.dll 177 | WINHTTP.dll: Facilitator.dll 178 | WINHTTP.dll: fdSSDP.dll 179 | WINHTTP.dll: FlightSettings.dll 180 | WINHTTP.dll: FontProvider.dll 181 | WINHTTP.dll: fveskybackup.dll 182 | WINHTTP.dll: HashtagDS.dll 183 | WINHTTP.dll: hnetcfg.dll 184 | WINHTTP.dll: httpprxm.dll 185 | WINHTTP.dll: HttpsDataSource.dll 186 | WINHTTP.dll: ieframe.dll 187 | WINHTTP.dll: igdDiag.dll 188 | WINHTTP.dll: inetpp.dll 189 | WINHTTP.dll: InstallService.dll 190 | WINHTTP.dll: invagent.dll 191 | WINHTTP.dll: ipnathlp.dll 192 | WINHTTP.dll: IppCommon.dll 193 | WINHTTP.dll: kerberos.dll 194 | WINHTTP.dll: laps.dll 195 | WINHTTP.dll: LicenseManager.dll 196 | WINHTTP.dll: LocationFramework.dll 197 | WINHTTP.dll: lpasvc.dll 198 | WINHTTP.dll: MapConfiguration.dll 199 | WINHTTP.dll: MapsStore.dll 200 | WINHTTP.dll: MbaeApi.dll 201 | WINHTTP.dll: McpManagementService.dll 202 | WINHTTP.dll: MCRecvSrc.dll 203 | WINHTTP.dll: MdmCommon.dll 204 | WINHTTP.dll: MdmDiagnostics.dll 205 | WINHTTP.dll: mdmregistration.dll 206 | WINHTTP.dll: mfnetcore.dll 207 | WINHTTP.dll: mfnetsrc.dll 208 | WINHTTP.dll: MicrosoftAccountTokenProvider.dll 209 | WINHTTP.dll: MicrosoftAccountWAMExtension.dll 210 | WINHTTP.dll: MitigationClient.dll 211 | WINHTTP.dll: moshostcore.dll 212 | WINHTTP.dll: mprddm.dll 213 | WINHTTP.dll: msdrm.dll 214 | WINHTTP.dll: mshtml.dll 215 | WINHTTP.dll: mstscax.dll 216 | WINHTTP.dll: msxml3.dll 217 | WINHTTP.dll: msxml6.dll 218 | WINHTTP.dll: NcaSvc.dll 219 | WINHTTP.dll: ncsi.dll 220 | WINHTTP.dll: netcorehc.dll 221 | WINHTTP.dll: netprofmsvc.dll 222 | WINHTTP.dll: networkhelper.dll 223 | WINHTTP.dll: NetworkMobileSettings.dll 224 | WINHTTP.dll: NetworkProxyCsp.dll 225 | WINHTTP.dll: ngcrecovery.dll 226 | WINHTTP.dll: nlahc.dll 227 | WINHTTP.dll: nlasvc.dll 228 | WINHTTP.dll: OneSettingsClient.dll 229 | WINHTTP.dll: pcasvc.dll 230 | WINHTTP.dll: pcwutl.dll 231 | WINHTTP.dll: PeerDistHttpTrans.dll 232 | WINHTTP.dll: prauthproviders.dll 233 | WINHTTP.dll: provcore.dll 234 | WINHTTP.dll: PushToInstall.dll 235 | WINHTTP.dll: qmgr.dll 236 | WINHTTP.dll: QualityUpdateAssistant.dll 237 | WINHTTP.dll: rasapi32.dll 238 | WINHTTP.dll: rascustom.dll 239 | WINHTTP.dll: rdpbase.dll 240 | WINHTTP.dll: RdpRelayTransport.dll 241 | WINHTTP.dll: ResetEngine.dll 242 | WINHTTP.dll: rpchttp.dll 243 | WINHTTP.dll: sdiagprv.dll 244 | WINHTTP.dll: sedplugins.dll 245 | WINHTTP.dll: SetProxyCredential.dll 246 | WINHTTP.dll: SettingsHandlers_nt.dll 247 | WINHTTP.dll: SettingSyncDownloadHelper.dll 248 | WINHTTP.dll: setupcln.dll 249 | WINHTTP.dll: sppcext.dll 250 | WINHTTP.dll: ssdpsrv.dll 251 | WINHTTP.dll: sstpsvc.dll 252 | WINHTTP.dll: storewuauth.dll 253 | WINHTTP.dll: StorSvc.dll 254 | WINHTTP.dll: svf.dll 255 | WINHTTP.dll: syncutil.dll 256 | WINHTTP.dll: tellib.dll 257 | WINHTTP.dll: TenantRestrictionsPlugin.dll 258 | WINHTTP.dll: TpmCoreProvisioning.dll 259 | WINHTTP.dll: TransportDSA.dll 260 | WINHTTP.dll: TSWorkspace.dll 261 | WINHTTP.dll: upnp.dll 262 | WINHTTP.dll: upnphost.dll 263 | WINHTTP.dll: upshared.dll 264 | WINHTTP.dll: UserDeviceRegistration.Ngc.dll 265 | WINHTTP.dll: WaaSAssessment.dll 266 | WINHTTP.dll: WaaSMedicSvc.dll 267 | WINHTTP.dll: wcmsvc.dll 268 | WINHTTP.dll: WebClnt.dll 269 | WINHTTP.dll: wer.dll 270 | WINHTTP.dll: whhelper.dll 271 | WINHTTP.dll: WiFiConfigSP.dll 272 | WINHTTP.dll: wifinetworkmanager.dll 273 | WINHTTP.dll: Win32CompatibilityAppraiserCSP.dll 274 | WINHTTP.dll: Windows.ApplicationModel.Store.dll 275 | WINHTTP.dll: Windows.Graphics.Printing.Workflow.dll 276 | WINHTTP.dll: Windows.Management.ModernDeployment.ConfigProviders.dll 277 | WINHTTP.dll: Windows.Management.Service.dll 278 | WINHTTP.dll: Windows.Media.Protection.PlayReady.dll 279 | WINHTTP.dll: Windows.Media.Streaming.dll 280 | WINHTTP.dll: Windows.Networking.Sockets.PushEnabledApplication.dll 281 | WINHTTP.dll: winethc.dll 282 | WINHTTP.dll: winhttpcom.dll 283 | WINHTTP.dll: winjson.dll 284 | WINHTTP.dll: winmde.dll 285 | WINHTTP.dll: winmsipc.dll 286 | WINHTTP.dll: WLanConn.dll 287 | WINHTTP.dll: WlanMediaManager.dll 288 | WINHTTP.dll: WlanMM.dll 289 | WINHTTP.dll: wlidsvc.dll 290 | WINHTTP.dll: wmp.dll 291 | WINHTTP.dll: WorkfoldersControl.dll 292 | WINHTTP.dll: workfolderssvc.dll 293 | WINHTTP.dll: Wpc.dll 294 | WINHTTP.dll: WpcDesktopMonSvc.dll 295 | WINHTTP.dll: WpcRefreshTask.dll 296 | WINHTTP.dll: WpcWebFilter.dll 297 | WINHTTP.dll: wpncore.dll 298 | WINHTTP.dll: wpnprv.dll 299 | WINHTTP.dll: wscapi.dll 300 | WINHTTP.dll: wscsvc.dll 301 | WINHTTP.dll: WSDApi.dll 302 | WINHTTP.dll: WsdProviderUtil.dll 303 | WINHTTP.dll: WsmSvc.dll 304 | WINHTTP.dll: wuaueng.dll 305 | WINHTTP.dll: wwansvc.dll 306 | WINHTTP.dll: XblAuthManager.dll 307 | WINHTTP.dll: XblGameSaveExt.dll 308 | WINHTTP.dll: XboxNetApiSvc.dll 309 | WsmSvc.DLL: pwrshplugin.dll 310 | WsmSvc.DLL: wecsvc.dll 311 | WsmSvc.DLL: wevtfwd.dll 312 | WsmSvc.DLL: winrscmd.dll 313 | WsmSvc.DLL: WsmAgent.dll 314 | WsmSvc.DLL: WSManMigrationPlugin.dll 315 | WsmSvc.DLL: WsmAuto.dll 316 | WsmSvc.DLL: WsmWmiPl.dll 317 | -------------------------------------------------------------------------------- /data/windows/dll-deps-research/dumpbin-delay-loads.ps1: -------------------------------------------------------------------------------- 1 | # Dumpbin Delay Loads program 2 | # 3 | # Allow PowerShell script: Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass 4 | # 5 | # PowerShell sucks, so clean up the output before commiting to Git repo: 6 | # dos2unix delay-loads.txt 7 | # - UTF-16LE with BOM -> UTF-8 (no BOM, unlike the PowerShell "utf8" option, which has BOM) 8 | 9 | # Define the input directory containing DLL files and the program output file 10 | $inputDirectory = "C:\Windows\System32" 11 | $outputFile = $env:USERPROFILE + "\Desktop\delay-loads.txt" 12 | 13 | # Dumpbin program path 14 | $dumpbinPath = "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\bin\Hostx64\x64\dumpbin.exe" 15 | 16 | # Create output file 17 | New-Item -Path $outputFile -ItemType File -ErrorAction Stop | Out-Null 18 | 19 | # Get all DLL files in the directory 20 | $dllFiles = Get-ChildItem -Path $inputDirectory -Filter *.dll 21 | 22 | # Iterate through DLL files 23 | foreach ($file in $dllFiles) { 24 | # Run dumpbin on DLL file and output into a file named according to the DLL name 25 | $outputLines = & $dumpbinPath /dependents $file.FullName /nologo 26 | 27 | $delayLoadsIdx = $outputLines.IndexOf(" Image has the following delay load dependencies:") 28 | if ($delayLoadsIdx -eq -1) { 29 | continue 30 | } 31 | 32 | # Get delay loads for DLL 33 | $output = "${file} Delay Loads:`n" 34 | # Skip empty line after delay load message to get DLL list 35 | $dllsIdx = $delayLoadsIdx + 2 36 | for ($i = $dllsIdx; $i -lt $outputLines.Length; $i++) { 37 | $line = $outputLines[$i] 38 | 39 | # Look for next empty line marking the end of delay loads 40 | if ($line -eq "") { 41 | break 42 | } 43 | 44 | $output = $output + $line + "`n" 45 | } 46 | 47 | $output | Out-File -FilePath $outputFile -Append -NoNewline 48 | } 49 | -------------------------------------------------------------------------------- /data/windows/dll-deps-research/dumpbin-dir.ps1: -------------------------------------------------------------------------------- 1 | # Dumpbin Directory program 2 | # 3 | # After dumping a directory of DLLs into separate per-DLL files, you can recursively grep through the output with grep -rin 4 | # Allow PowerShell script: Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass 5 | 6 | # Define the input directory containing DLL files and the dumpbin output directory 7 | $inputDirectory = "C:\Windows\System32" 8 | $outputDirectory = $env:USERPROFILE + "\Desktop\dumpbin-imports" 9 | 10 | # Dumpbin program path 11 | $dumpbinPath = "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\bin\Hostx64\x64\dumpbin.exe" 12 | $dumpbinOption = "/imports" # "/imports", "/exports", etc. 13 | 14 | # Create directory if it doesn't exist 15 | New-Item -Path $outputDirectory -ItemType Directory -Force | Out-Null 16 | 17 | # Get all DLL files in the directory 18 | $dllFiles = Get-ChildItem -Path $inputDirectory -Filter *.dll 19 | 20 | # Iterate through DLL files 21 | foreach ($file in $dllFiles) { 22 | Write-Host "Processing ${file}..." 23 | # Run dumpbin on DLL file and output into a file named according to the DLL name 24 | & $dumpbinPath $dumpbinOption $file.FullName /out:$outputDirectory\$($file.BaseName).txt /nologo 25 | } 26 | -------------------------------------------------------------------------------- /data/windows/dll-deps-research/dumpbin-find-dlls-depending-on-dlls.ps1: -------------------------------------------------------------------------------- 1 | # Dumpbin DLLs Depending on DLLs 2 | # 3 | # For each DLL in a file, search for DLLs that depend on it 4 | # Allow PowerShell script: Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass 5 | # 6 | # NOTE: This script only takes into account one dependency layer 7 | # It will find the DLLs depending on a given DLL; however, it cannot find deeper dependencies such as a DLL depending on a DLL which then depends on the given DLL we are searching for 8 | 9 | # Define the input file containing DLL file paths and the output directory 10 | $inputFile = $env:USERPROFILE + "\Desktop\service-dlls.txt" 11 | $outputDirectory = $env:USERPROFILE + "\Desktop\dlls-depending-on-service-dlls" 12 | 13 | # Pre-processed Dumpbin output directory of all DLLs to scan 14 | # Get this pre-processed data by running dumpbin-dir.ps1 first 15 | # For us, this is all DLLs in C:\Windows\System32 (not including subdirectories) 16 | $dumpbinAllOutputDirectory = $env:USERPROFILE + "\Desktop\dumpbin-imports" 17 | 18 | Set-Location -Path $dumpbinAllOutputDirectory 19 | 20 | # Create directory if it doesn't exist 21 | New-Item -Path $outputDirectory -ItemType Directory -Force | Out-Null 22 | 23 | $dumpbinOutput = Get-Item $dumpbinAllOutputDirectory 24 | 25 | # Iterate through DLL files list 26 | foreach ($inputLine in Get-Content $inputFile) { 27 | #$inputLine = "C:\Windows\System32\dhcpcsvc.dll" 28 | $queryDll = Get-Item $inputLine 29 | #Write-Host "Searching for DLLs with dependencies on: ${queryDll.FullName}..." 30 | 31 | # Search for DLLs importing given DLL name 32 | foreach ($dumpbinOutputLine in $dumpbinOutput) { 33 | # Output format: DLL depended on: DLL taking dependency 34 | # Regex is for getting basename so output doesn't include the .txt extension given to dumpbin-dir.ps1 output data files 35 | Select-String -Path * -Pattern " $($queryDll.Name)$" | ForEach-Object { "$($_.Line.Trim()): $($_.Filename -replace '\.[^.]+$')" } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /data/windows/dll-deps-research/get-service-dlls.ps1: -------------------------------------------------------------------------------- 1 | # Run this as administrator 2 | # 3 | # PowerShell sucks, so clean up the output before commiting to Git repo: 4 | # dos2unix service-dlls.txt 5 | # - UTF-16LE with BOM -> UTF-8 (no BOM, unlike the PowerShell "utf8" option, which has BOM) and CRLF -> LF 6 | Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Services\*\Parameters | Where-Object { $_.ServiceDll } | Select-Object -ExpandProperty ServiceDll | Out-File -FilePath "service-dlls.txt" -NoNewline 7 | -------------------------------------------------------------------------------- /data/windows/dll-deps-research/process-count.ps1: -------------------------------------------------------------------------------- 1 | $processName = "svchost" 2 | 3 | $processes = Get-Process 4 | $searchCount = ($processes | Where-Object { $_.ProcessName -eq $processName }).Count 5 | $totalCount = $processes.Count 6 | Write-Host "$processName processes: $searchCount / $totalCount = " ($searchCount / $totalCount) 7 | -------------------------------------------------------------------------------- /data/windows/dll-deps-research/service-dlls.txt: -------------------------------------------------------------------------------- 1 | C:\Windows\System32\AarSvc.dll 2 | C:\Windows\System32\AJRouter.dll 3 | C:\Windows\System32\appidsvc.dll 4 | C:\Windows\System32\appinfo.dll 5 | C:\Windows\System32\appmgmts.dll 6 | C:\Windows\system32\AppReadiness.dll 7 | C:\Windows\system32\appxdeploymentserver.dll 8 | C:\Windows\System32\assignedaccessmanagersvc.dll 9 | C:\Windows\System32\AudioEndpointBuilder.dll 10 | C:\Windows\System32\Audiosrv.dll 11 | C:\Windows\System32\autotimesvc.dll 12 | C:\Windows\System32\AxInstSV.dll 13 | C:\Windows\System32\BcastDVRUserService.dll 14 | C:\Windows\System32\bdesvc.dll 15 | C:\Windows\System32\bfe.dll 16 | C:\Windows\System32\qmgr.dll 17 | C:\Windows\System32\Microsoft.Bluetooth.UserService.dll 18 | C:\Windows\System32\psmsrv.dll 19 | C:\Windows\System32\BTAGService.dll 20 | C:\Windows\System32\BthAvctpSvc.dll 21 | C:\Windows\system32\bthserv.dll 22 | C:\Windows\system32\CapabilityAccessManager.dll 23 | C:\Windows\System32\CaptureService.dll 24 | C:\Windows\System32\cbdhsvc.dll 25 | C:\Windows\System32\CDPSvc.dll 26 | C:\Windows\System32\CDPUserSvc.dll 27 | C:\Windows\System32\certprop.dll 28 | C:\Windows\System32\ClipSVC.dll 29 | C:\Windows\system32\cloudidsvc.dll 30 | C:\Windows\System32\ConsentUxClient.dll 31 | C:\Windows\system32\coremessaging.dll 32 | C:\Windows\system32\cryptsvc.dll 33 | C:\Windows\System32\cscsvc.dll 34 | C:\Windows\system32\rpcss.dll 35 | C:\Windows\system32\dcsvc.dll 36 | C:\Windows\System32\defragsvc.dll 37 | C:\Windows\System32\deviceaccess.dll 38 | C:\Windows\system32\das.dll 39 | C:\Windows\system32\umpnpmgr.dll 40 | C:\Windows\System32\Windows.Devices.Picker.dll 41 | C:\Windows\System32\DevicesFlowBroker.dll 42 | C:\Windows\system32\DevQueryBroker.dll 43 | C:\Windows\system32\dhcpcore.dll 44 | C:\Windows\system32\DiagSvc.dll 45 | C:\Windows\system32\diagtrack.dll 46 | C:\Windows\System32\DialogBlockingService.dll 47 | C:\Windows\System32\DispBroker.Desktop.dll 48 | C:\Windows\system32\Microsoft.Graphics.Display.DisplayEnhancementService.dll 49 | C:\Windows\system32\Windows.Internal.Management.dll 50 | C:\Windows\system32\dmwappushsvc.dll 51 | C:\Windows\System32\dnsrslvr.dll 52 | C:\Windows\System32\dot3svc.dll 53 | C:\Windows\system32\dps.dll 54 | C:\Windows\System32\DeviceSetupManager.dll 55 | C:\Windows\System32\DsSvc.dll 56 | C:\Windows\System32\dusmsvc.dll 57 | C:\Windows\System32\eapsvc.dll 58 | C:\Windows\system32\efssvc.dll 59 | C:\Windows\System32\embeddedmodesvc.dll 60 | C:\Windows\system32\EnterpriseAppMgmtSvc.dll 61 | C:\Windows\System32\wevtsvc.dll 62 | C:\Windows\system32\es.dll 63 | C:\Windows\system32\fdPHost.dll 64 | C:\Windows\system32\fdrespub.dll 65 | C:\Windows\system32\fhsvc.dll 66 | C:\Windows\system32\FntCache.dll 67 | C:\Windows\system32\FrameServer.dll 68 | C:\Windows\System32\gpsvc.dll 69 | C:\Windows\System32\GraphicsPerfSvc.dll 70 | C:\Windows\system32\hidserv.dll 71 | C:\Windows\system32\ListSvc.dll 72 | C:\Windows\system32\provsvc.dll 73 | C:\Windows\System32\hvhostsvc.dll 74 | C:\Windows\System32\tetheringservice.dll 75 | C:\Windows\System32\ikeext.dll 76 | C:\Windows\system32\InstallService.dll 77 | C:\Windows\System32\iphlpsvc.dll 78 | C:\Windows\System32\IpxlatCfg.dll 79 | C:\Windows\system32\keyiso.dll 80 | C:\Windows\system32\msdtckrm.dll 81 | C:\Windows\system32\srvsvc.dll 82 | C:\Windows\System32\wkssvc.dll 83 | C:\Windows\System32\lfsvc.dll 84 | C:\Windows\system32\LicenseManagerSvc.dll 85 | C:\Windows\System32\lltdsvc.dll 86 | C:\Windows\System32\lmhsvc.dll 87 | C:\Windows\System32\lsm.dll 88 | C:\Windows\System32\LanguageOverlayServer.dll 89 | C:\Windows\System32\moshost.dll 90 | C:\Windows\System32\McpManagementService.dll 91 | C:\Windows\System32\MessagingService.dll 92 | C:\Windows\System32\MixedRealityRuntime.dll 93 | C:\Windows\system32\mpssvc.dll 94 | C:\Windows\system32\iscsiexe.dll 95 | C:\Windows\System32\KeyboardFilterSvc.dll 96 | C:\Windows\System32\NaturalAuth.dll 97 | C:\Windows\System32\ncasvc.dll 98 | C:\Windows\System32\ncbservice.dll 99 | C:\Windows\System32\NcdAutoSetup.dll 100 | C:\Windows\system32\netlogon.dll 101 | C:\Windows\System32\netman.dll 102 | C:\Windows\System32\netprofmsvc.dll 103 | C:\Windows\System32\NetSetupSvc.dll 104 | C:\Windows\System32\NgcCtnrSvc.dll 105 | C:\Windows\system32\ngcsvc.dll 106 | C:\Windows\System32\nlasvc.dll 107 | C:\Windows\system32\nsisvc.dll 108 | C:\Windows\System32\APHostService.dll 109 | C:\Windows\system32\pnrpsvc.dll 110 | C:\Windows\system32\p2psvc.dll 111 | C:\Windows\System32\pcasvc.dll 112 | C:\Windows\system32\peerdistsvc.dll 113 | C:\Windows\System32\PhoneService.dll 114 | C:\Windows\System32\PimIndexMaintenance.dll 115 | C:\Windows\system32\pla.dll 116 | C:\Windows\system32\umpnpmgr.dll 117 | C:\Windows\system32\pnrpauto.dll 118 | C:\Windows\system32\pnrpsvc.dll 119 | C:\Windows\System32\ipsecsvc.dll 120 | C:\Windows\system32\umpo.dll 121 | C:\Windows\system32\spool\drivers\x64\3\PrintConfig.dll 122 | C:\Windows\System32\PrintWorkflowService.dll 123 | C:\Windows\system32\profsvc.dll 124 | C:\Windows\system32\PushToInstall.dll 125 | C:\Windows\system32\qwave.dll 126 | C:\Windows\System32\rasauto.dll 127 | C:\Windows\System32\rasmans.dll 128 | C:\Windows\System32\mprdim.dll 129 | C:\Windows\system32\regsvc.dll 130 | C:\Windows\system32\RDXService.dll 131 | C:\Windows\System32\RMapi.dll 132 | C:\Windows\System32\RpcEpMap.dll 133 | C:\Windows\system32\rpcss.dll 134 | C:\Windows\System32\SCardSvr.dll 135 | C:\Windows\System32\ScDeviceEnum.dll 136 | C:\Windows\system32\schedsvc.dll 137 | C:\Windows\System32\certprop.dll 138 | C:\Windows\System32\SDRSVC.dll 139 | C:\Windows\system32\seclogon.dll 140 | C:\Windows\system32\SEMgrSvc.dll 141 | C:\Windows\System32\sens.dll 142 | C:\Windows\system32\SensorService.dll 143 | C:\Windows\system32\sensrsvc.dll 144 | C:\Windows\system32\sessenv.dll 145 | C:\Windows\System32\ipnathlp.dll 146 | C:\Windows\System32\SharedRealitySvc.dll 147 | C:\Windows\System32\shsvcs.dll 148 | C:\Windows\system32\Windows.SharedPC.AccountManager.dll 149 | C:\Windows\System32\smphost.dll 150 | C:\Windows\system32\SmsRouterSvc.dll 151 | C:\Windows\System32\ssdpsrv.dll 152 | C:\Windows\system32\sstpsvc.dll 153 | C:\Windows\system32\windows.staterepository.dll 154 | C:\Windows\System32\wiaservc.dll 155 | C:\Windows\system32\storsvc.dll 156 | C:\Windows\system32\svsvc.dll 157 | C:\Windows\System32\swprv.dll 158 | C:\Windows\system32\sysmain.dll 159 | C:\Windows\System32\SystemEventsBrokerServer.dll 160 | C:\Windows\System32\TabSvc.dll 161 | C:\Windows\System32\tapisrv.dll 162 | C:\Windows\System32\termsrv.dll 163 | C:\Windows\system32\themeservice.dll 164 | C:\Windows\System32\TimeBrokerServer.dll 165 | C:\Windows\System32\TokenBroker.dll 166 | C:\Windows\System32\trkwks.dll 167 | C:\Windows\system32\MitigationClient.dll 168 | C:\Windows\system32\tzautoupdate.dll 169 | C:\Windows\System32\windowsudk.shellcommon.dll 170 | C:\Windows\System32\umrdp.dll 171 | C:\Windows\System32\unistore.dll 172 | C:\Windows\System32\upnphost.dll 173 | C:\Windows\System32\userdataservice.dll 174 | C:\Windows\System32\usermgr.dll 175 | C:\Windows\system32\usosvc.dll 176 | C:\Windows\System32\vac.dll 177 | C:\Windows\System32\vaultsvc.dll 178 | C:\Windows\System32\icsvc.dll 179 | C:\Windows\System32\icsvc.dll 180 | C:\Windows\System32\icsvc.dll 181 | C:\Windows\System32\icsvcext.dll 182 | C:\Windows\System32\icsvc.dll 183 | C:\Windows\System32\icsvc.dll 184 | C:\Windows\System32\icsvc.dll 185 | C:\Windows\System32\icsvcext.dll 186 | C:\Windows\system32\w32time.dll 187 | C:\Windows\System32\WaaSMedicSvc.dll 188 | C:\Windows\system32\WalletService.dll 189 | C:\Windows\System32\Windows.WARP.JITService.dll 190 | C:\Windows\System32\wbiosrvc.dll 191 | C:\Windows\System32\wcmsvc.dll 192 | C:\Windows\System32\wcncsvc.dll 193 | C:\Windows\system32\wdi.dll 194 | C:\Windows\system32\wdi.dll 195 | C:\Windows\System32\webclnt.dll 196 | C:\Windows\system32\wecsvc.dll 197 | C:\Windows\system32\wephostsvc.dll 198 | C:\Windows\System32\wercplsupport.dll 199 | C:\Windows\System32\WerSvc.dll 200 | C:\Windows\System32\wfdsconmgrsvc.dll 201 | C:\Windows\System32\wiarpc.dll 202 | C:\Windows\system32\winhttp.dll 203 | C:\Windows\system32\wbem\WMIsvc.dll 204 | C:\Windows\system32\WsmSvc.dll 205 | C:\Windows\system32\flightsettings.dll 206 | C:\Windows\System32\wlansvc.dll 207 | C:\Windows\system32\wlidsvc.dll 208 | C:\Windows\System32\lpasvc.dll 209 | C:\Windows\system32\Windows.Management.Service.dll 210 | C:\Windows\system32\workfolderssvc.dll 211 | C:\Windows\System32\WpcDesktopMonSvc.dll 212 | C:\Windows\system32\wpdbusenum.dll 213 | C:\Windows\system32\WpnService.dll 214 | C:\Windows\System32\WpnUserService.dll 215 | C:\Windows\System32\wscsvc.dll 216 | C:\Windows\system32\wuaueng.dll 217 | C:\Windows\System32\wwansvc.dll 218 | C:\Windows\System32\XblAuthManager.dll 219 | C:\Windows\System32\XblGameSave.dll 220 | C:\Windows\System32\XboxGipSvc.dll 221 | C:\Windows\system32\XboxNetApiSvc.dll 222 | 223 | -------------------------------------------------------------------------------- /data/windows/ms-devblogs-search/README.md: -------------------------------------------------------------------------------- 1 | # Microsoft Developer Blogs Search 2 | 3 | This sections provides the outputs of grepping Old New Thing articles for the terms "loader lock" and "DllMain". The comprehensive search is helpful for learning about the relevant Windows internals, backstory, and for reverse engineering. 4 | 5 | For the search script, please see the [Microsoft Developer Blogs Search repo](https://github.com/ElliotKillick/ms-devblogs-search). 6 | 7 | Grep articles: 8 | 9 | ```shell 10 | grep -rin -B5 -A5 DllMain > ../dllmain.txt 11 | ``` 12 | 13 | ```shell 14 | grep -rin -B5 -A5 "loader lock" > ../loader-lock.txt 15 | ``` 16 | -------------------------------------------------------------------------------- /data/windows/ms-docs-search/README.md: -------------------------------------------------------------------------------- 1 | # Microsoft Documentaion Search 2 | 3 | This section intends to document all the places where Microsoft documentation mentions `DllMain` or loader lock. 4 | 5 | Get Microsoft documentation repos: 6 | 7 | ``` 8 | git clone https://github.com/MicrosoftDocs/win32 9 | git clone https://github.com/MicrosoftDocs/sdk-api 10 | git clone https://github.com/MicrosoftDocs/cpp-docs 11 | ``` 12 | 13 | Grep each repo: 14 | 15 | ```shell 16 | grep -rIni DllMain > ../ 17 | ``` 18 | 19 | ```shell 20 | grep -rIni "loader lock" > ../ 21 | ``` 22 | -------------------------------------------------------------------------------- /data/windows/ms-docs-search/cpp-docs-loader-lock.txt: -------------------------------------------------------------------------------- 1 | docs/error-messages/compiler-warnings/compiler-warnings-by-compiler-version.md:699:| C4747 | `Calling managed 'type': Managed code may not be run under loader lock, including the DLL entrypoint and calls reached from the DLL entrypoint` | 2 | docs/error-messages/compiler-warnings/compiler-warning-level-1-c4747.md:11:Calling managed 'entrypoint': Managed code may not be run under loader lock, including the DLL entrypoint and calls reached from the DLL entrypoint 3 | docs/error-messages/compiler-warnings/compiler-warnings-c4600-through-c4799.md:138:|[Compiler warning (level 1) C4747](../../error-messages/compiler-warnings/compiler-warning-level-1-c4747.md)|Calling managed 'entrypoint': Managed code may not be run under loader lock, including the DLL entrypoint and calls reached from the DLL entrypoint| 4 | docs/error-messages/tool-errors/c-runtime-error-r6031.md:25:This diagnostic indicates that MSIL instructions were executing during loader lock. For more information, see [Initialization of Mixed Assemblies](../../dotnet/initialization-of-mixed-assemblies.md). 5 | docs/error-messages/tool-errors/c-runtime-error-r6033.md:25:This diagnostic indicates that MSIL instructions were executing during loader lock. This can occur if you have compiled native C++ by using the /clr flag. Only use the /clr flag on modules that contain managed code. For more information, see [Initialization of Mixed Assemblies](../../dotnet/initialization-of-mixed-assemblies.md). 6 | docs/dotnet/how-to-migrate-to-clr.md:108:### Loader lock deadlock 7 | docs/dotnet/how-to-migrate-to-clr.md:110:The "loader lock deadlock" can occur, but is deterministic and is detected and reported at runtime. See [Initialization of Mixed Assemblies](../dotnet/initialization-of-mixed-assemblies.md) for detailed background, guidance, and solutions. 8 | docs/dotnet/library-support-for-mixed-assemblies.md:29:- Resolves the loader lock issues that applied to mixed DLLs compiled in Visual Studio 2003 and earlier. 9 | docs/dotnet/initialization-of-mixed-assemblies.md:5:helpviewer_keywords: ["mixed assemblies [C++], loader lock", "loader lock [C++]", "initializing mixed assemblies", "deadlocks [C++]", ".cctor [C++]", "custom locales [C++]", "mixed assemblies [C++], initilizing"] 10 | docs/dotnet/initialization-of-mixed-assemblies.md:10:Windows developers must always be wary of loader lock when running code during `DllMain`. However, there are some additional issues to consider when dealing with C++/CLI mixed-mode assemblies. 11 | docs/dotnet/initialization-of-mixed-assemblies.md:14:## Causes of Loader Lock 12 | docs/dotnet/initialization-of-mixed-assemblies.md:20:The Windows loader guarantees that no code can access code or data in that DLL before it's been initialized. And it ensures that no code can redundantly load the DLL while it's partially initialized. To do it, the Windows loader uses a process-global critical section (often called the "loader lock") that prevents unsafe access during module initialization. As a result, the loading process is vulnerable to many classic deadlock scenarios. For mixed assemblies, the following two scenarios increase the risk of deadlock: 13 | docs/dotnet/initialization-of-mixed-assemblies.md:22:- First, if users attempt to execute functions compiled to Microsoft intermediate language (MSIL) when the loader lock is held (from `DllMain` or in static initializers, for example), it can cause deadlock. Consider the case in which the MSIL function references a type in an assembly that's not loaded yet. The CLR will attempt to automatically load that assembly, which may require the Windows loader to block on the loader lock. A deadlock occurs, since the loader lock is already held by code earlier in the call sequence. However, executing MSIL under loader lock doesn't guarantee that a deadlock will occur. That's what makes this scenario difficult to diagnose and fix. In some circumstances, such as when the DLL of the referenced type contains no native constructs and all of its dependencies contain no native constructs, the Windows loader isn't required to load the .NET assembly of the referenced type. Additionally, the required assembly or its mixed native/.NET dependencies may have already been loaded by other code. Consequently, the deadlocking can be difficult to predict, and can vary depending on the configuration of the target machine. 14 | docs/dotnet/initialization-of-mixed-assemblies.md:24:- Second, when loading DLLs in versions 1.0 and 1.1 of the .NET Framework, the CLR assumed that the loader lock wasn't held, and took several actions that are invalid under loader lock. Assuming that the loader lock isn't held is a valid assumption for purely .NET DLLs. But because mixed DLLs execute native initialization routines, they require the native Windows loader, and consequently the loader lock. So, even if the developer wasn't attempting to execute any MSIL functions during DLL initialization, there was still a small possibility of nondeterministic deadlock in .NET Framework versions 1.0 and 1.1. 15 | docs/dotnet/initialization-of-mixed-assemblies.md:32:Loader lock can still occur, but now it occurs reproducibly, and is detected. If `DllMain` contains MSIL instructions, the compiler generates warning [Compiler Warning (level 1) C4747](../error-messages/compiler-warnings/compiler-warning-level-1-c4747.md). Furthermore, either the CRT or the CLR will try to detect and report attempts to execute MSIL under loader lock. CRT detection results in runtime diagnostic C Run-Time Error R6033. 16 | docs/dotnet/initialization-of-mixed-assemblies.md:34:The rest of this article describes the remaining scenarios for which MSIL can execute under the loader lock. It shows how to resolve the issue under each of those scenarios, and debugging techniques. 17 | docs/dotnet/initialization-of-mixed-assemblies.md:38:There are several different situations under which user code can execute MSIL under loader lock. The developer must ensure that the user code implementation doesn't attempt to execute MSIL instructions under each of these circumstances. The following subsections describe all possibilities with a discussion of how to resolve issues in the most common cases. 18 | docs/dotnet/initialization-of-mixed-assemblies.md:42:The `DllMain` function is a user-defined entry point for a DLL. Unless the user specifies otherwise, `DllMain` is invoked every time a process or thread attaches to or detaches from the containing DLL. Since this invocation can occur while the loader lock is held, no user-supplied `DllMain` function should be compiled to MSIL. Furthermore, no function in the call tree rooted at `DllMain` can be compiled to MSIL. To resolve issues here, the code block that defines `DllMain` should be modified with `#pragma unmanaged`. The same should be done for every function that `DllMain` calls. 19 | docs/dotnet/initialization-of-mixed-assemblies.md:46:As an alternative, if `DllMain` isn't required or if it doesn't need to be executed under loader lock, you can remove the user-provided `DllMain` implementation, which eliminates the problem. 20 | docs/dotnet/initialization-of-mixed-assemblies.md:65:This risk of deadlock depends on whether the containing module is compiled with **`/clr`** and whether MSIL will be executed. Specifically, if the static variable is compiled without **`/clr`** (or is in a `#pragma unmanaged` block), and the dynamic initializer required to initialize it results in the execution of MSIL instructions, deadlock may occur. It's because, for modules compiled without **`/clr`**, the initialization of static variables is performed by DllMain. In contrast, static variables compiled with **`/clr`** are initialized by the `.cctor`, after the unmanaged initialization stage has completed and the loader lock has been released. 21 | docs/dotnet/initialization-of-mixed-assemblies.md:79:If the user-provided versions are compiled to MSIL, then these initializers will be attempting to execute MSIL instructions while the loader lock is held. A user-supplied `malloc` has the same consequences. To resolve this problem, any of these overloads or user-supplied definitions must be implemented as native code using the `#pragma unmanaged` directive. 22 | docs/dotnet/initialization-of-mixed-assemblies.md:85:If the user provides a custom global locale, this locale gets used to initialize all future I/O streams, including streams that are statically initialized. If this global locale object is compiled to MSIL, then locale-object member functions compiled to MSIL may be invoked while the loader lock is held. 23 | docs/dotnet/initialization-of-mixed-assemblies.md:89:The source files containing all global I/O stream definitions can be compiled using the **`/clr`** option. It prevents their static initializers from being executed under loader lock. 24 | docs/dotnet/initialization-of-mixed-assemblies.md:93:Refrain from setting the custom locale as the global locale until after the loader lock is released. Then explicitly configure I/O streams created during initialization with the custom locale. 25 | docs/dotnet/initialization-of-mixed-assemblies.md:105:Because the same header may be included both by C++ files with **`/clr`** enabled and disabled, or a #include can be wrapped inside a `#pragma unmanaged` block, it's possible to have both MSIL and native versions of functions that provide implementations in headers. MSIL and native implementations have different semantics for initialization under the loader lock, which effectively violates the one definition rule. Consequently, when the linker chooses the largest implementation, it may choose the MSIL version of a function, even if it was explicitly compiled to native code elsewhere using the `#pragma unmanaged` directive. To ensure that an MSIL version of a template or inline function is never called under loader lock, every definition of every such function called under loader lock must be modified with the `#pragma unmanaged` directive. If the header file is from a third party, the easiest way to make this change is to push and pop the `#pragma unmanaged` directive around the #include directive for the offending header file. (See [managed, unmanaged](../preprocessor/managed-unmanaged.md) for an example.) However, this strategy doesn't work for headers that contain other code that must directly call .NET APIs. 26 | docs/dotnet/initialization-of-mixed-assemblies.md:107:As a convenience for users dealing with loader lock, the linker will choose the native implementation over the managed when presented with both. This default avoids the above issues. However, there are two exceptions to this rule in this release because of two unresolved issues with the compiler: 27 | docs/dotnet/initialization-of-mixed-assemblies.md:131:All diagnoses of loader lock problems should be done with Debug builds. Release builds may not produce diagnostics. And, the optimizations made in Release mode may mask some of the MSIL under loader lock scenarios. 28 | docs/dotnet/initialization-of-mixed-assemblies.md:133:## How to debug loader lock issues 29 | docs/dotnet/initialization-of-mixed-assemblies.md:137:To identify the specific MSIL function that was called under loader lock, developers should complete the following steps: 30 | docs/dotnet/initialization-of-mixed-assemblies.md:161:1. Look for the first instance (closest to the bottom of the stack) of either _CorDllMain (if `DllMain` causes the issue) or _VTableBootstrapThunkInitHelperStub or GetTargetForVTableEntry (if a static initializer causes the issue). The stack entry just below this call is the invocation of the MSIL implemented function that attempted to execute under loader lock. 31 | docs/dotnet/initialization-of-mixed-assemblies.md:169:The following sample shows how to avoid loader lock by moving code from `DllMain` into the constructor of a global object. 32 | docs/dotnet/mixed-native-and-managed-assemblies.md:27:Describes the "loader lock" problem and solutions. 33 | -------------------------------------------------------------------------------- /data/windows/ms-docs-search/sdk-api-loader-lock.txt: -------------------------------------------------------------------------------- 1 | sdk-api-src/content/shellapi/nf-shellapi-shellexecuteexw.md:88:When DLLs are loaded into your process, you acquire a lock known as a loader lock. The DllMain function always executes under the loader lock. It is important that you do not call ShellExecuteEx while you hold a loader lock. Because ShellExecuteEx is extensible, you could load code that does not function properly in the presence of a loader lock, risking a deadlock and therefore an unresponsive thread. 2 | sdk-api-src/content/shellapi/nf-shellapi-shellexecuteexa.md:88:When DLLs are loaded into your process, you acquire a lock known as a loader lock. The DllMain function always executes under the loader lock. It is important that you do not call ShellExecuteEx while you hold a loader lock. Because ShellExecuteEx is extensible, you could load code that does not function properly in the presence of a loader lock, risking a deadlock and therefore an unresponsive thread. 3 | sdk-api-src/content/evntrace/nc-evntrace-wmidprequest.md:141:specifically, anything that requires a loader lock). 4 | sdk-api-src/content/evntprov/nc-evntprov-penablecallback.md:244:> anything that requires the process's loader lock, i.e. it must not directly or 5 | sdk-api-src/content/winnt/nf-winnt-tpsetcallbackracewithdll.md:67:You should call this function if a callback might acquire the loader lock. This prevents a deadlock from occurring when one thread in DllMain is waiting for the callback to end, and another thread that is executing the callback attempts to acquire the loader lock. 6 | sdk-api-src/content/winnt/nf-winnt-tpsetcallbackracewithdll.md:71:Managing callbacks created with a TP_CALLBACK_ENVIRON that specifies a callback library is somewhat processing-intensive. You should consider other options for ensuring that the library is not unloaded while callbacks are executing, or to guarantee that callbacks which may be executing do not acquire the loader lock. 7 | sdk-api-src/content/winbase/nf-winbase-setthreadpoolcallbacklibrary.md:67:You should call this function if a callback might acquire the loader lock. This prevents a deadlock from occurring when one thread in DllMain is waiting for the callback to end, and another thread that is executing the callback attempts to acquire the loader lock. 8 | sdk-api-src/content/winbase/nf-winbase-setthreadpoolcallbacklibrary.md:71:Managing callbacks created with a TP_CALLBACK_ENVIRON that specifies a callback library is somewhat processing-intensive. You should consider other options for ensuring that the library is not unloaded while callbacks are executing, or to guarantee that callbacks which may be executing do not acquire the loader lock. 9 | sdk-api-src/content/winver/nf-winver-getfileversioninfoexw.md:106:Indicates a preference for version.dll to attempt to preload the image outside of the loader lock to avoid contention. This flag does not change the behavior or semantics of the function. 10 | sdk-api-src/content/winver/nf-winver-getfileversioninfoexa.md:106:Indicates a preference for version.dll to attempt to preload the image outside of the loader lock to avoid contention. This flag does not change the behavior or semantics of the function. 11 | -------------------------------------------------------------------------------- /data/windows/ms-docs-search/win32-loader-lock.txt: -------------------------------------------------------------------------------- 1 | desktop-src/Win7AppQual/preventing-hangs-in-windows-applications.md:111:Worse yet, the operating system has its own internal process-specific lock that sometimes is held while your code executes. This lock is acquired when DLLs are loaded into the process, and is therefore called the 'loader lock.' The DllMain function always executes under the loader lock; if you acquire any locks in DllMain (and you should not), you need to make the loader lock part of your lock order. Calling certain Win32 APIs might also acquire the loader lock on your behalf - functions like LoadLibraryEx, GetModuleHandle, and especially CoCreateInstance. 2 | desktop-src/Win7AppQual/preventing-hangs-in-windows-applications.md:113:To tie all of this together, look at the sample code below. This function acquires multiple synchronization objects and implicitly defines a lock order, something that is not necessarily obvious on cursory inspection. On function entry, the code acquires a Critical Section and does not release it until function exit, thereby making it the top node in our lock hierarchy. The code then calls the Win32 function LoadIcon(), which under the covers might call into the Operating System Loader to load this binary. This operation would acquire the loader lock, which now also becomes part of this lock hierarchy (make sure the DllMain function does not acquire the g\_cs lock). Next the code calls SendMessage(), a blocking cross-thread operation, which will not return unless the UI thread responds. Again, make sure that the UI thread never acquires g\_cs. 3 | desktop-src/Win7AppQual/preventing-hangs-in-windows-applications.md:132:- Design a lock hierarchy and obey it. Add all the necessary locks. There are many more synchronization primitives than just Mutex and CriticalSections; they all need to be included. Include the loader lock in your hierarchy if you take any locks in DllMain() 4 | desktop-src/Win7AppQual/preventing-hangs-in-windows-applications.md:137:- Be careful when waiting on a thread handle from a DLL. Always assume that your code could be called under the loader lock. It's better to reference-count your resources and let the worker thread do its own cleanup (and then use FreeLibraryAndExitThread to terminate cleanly) 5 | desktop-src/Win7AppQual/preventing-hangs-in-windows-applications.md:144:- Do any work in the constructors and destructors for global variables, they are executed under the loader lock 6 | desktop-src/DevNotes/ldrfastfailinloadercallout.md:43:This routine does not catch all potential deadlock cases; it is possible for a thread inside a loader callout to acquire a lock while some thread outside a loader callout holds the same lock and makes a call into the loader. In other words, there can be a lock order inversion between the loader lock and a client lock. 7 | desktop-src/shell/ctf.md:55:|
**CTF\_NOADDREFLIB**
0x00002000
| 0x00002000. **Windows 7 or later.** This flag is essentially the opposite of CTF\_FREELIBANDEXIT. This avoids [**LoadLibrary**](/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya)/[**FreeLibraryAndExitThread**](/windows/win32/api/libloaderapi/nf-libloaderapi-freelibraryandexitthread) calls that can result in contention for the loader lock. Use CTF\_NOADDREFLIB only when the new thread has means to ensure that the code of the original thread procedure will remain loaded. This value should not be used in the context of COM objects, because COM objects must ensure that the DLL stays loaded (normally, COM unloads the DLLs).
| 8 | desktop-src/ProcThread/user-mode-scheduling.md:89:- To help prevent deadlocks, the UMS scheduler thread should not share locks with UMS worker threads. This includes both application-created locks and system locks that are acquired indirectly by operations such as allocating from the heap or loading DLLs. For example, suppose the scheduler runs a UMS worker thread that loads a DLL. The worker thread acquires the loader lock and blocks. The system calls the scheduler entry point function, which then loads a DLL. This causes a deadlock, because the loader lock is already held and cannot be released until the first thread unblocks. To help avoid this problem, delegate work that might share locks with UMS worker threads to a dedicated UMS worker thread or a non-UMS thread. 9 | desktop-src/Dlls/dllmain.md:131:During initial process startup or after a call to [**LoadLibrary**](/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya), the system scans the list of loaded DLLs for the process. For each DLL that has not already been called with the **DLL\_PROCESS\_ATTACH** value, the system calls the DLL's entry-point function. This call is made in the context of the thread that caused the process address space to change, such as the primary thread of the process or the thread that called **LoadLibrary**. Access to the entry point is serialized by the system on a process-wide basis. Threads in *DllMain* hold the loader lock so no additional DLLs can be dynamically loaded or initialized. 10 | desktop-src/Dlls/dynamic-link-library-best-practices.md:29:[**DllMain**](dllmain.md) is called while the loader-lock is held. Therefore, significant restrictions are imposed on the functions that can be called within **DllMain**. As such, **DllMain** is designed to perform minimal initialization tasks, by using a small subset of the Microsoft® Windows® API. You cannot call any function in **DllMain** that directly or indirectly tries to acquire the loader lock. Otherwise, you will introduce the possibility that your application deadlocks or crashes. An error in a **DllMain** implementation can jeopardize the entire process and all of its threads. 11 | desktop-src/Dlls/dynamic-link-library-best-practices.md:31:The ideal [**DllMain**](dllmain.md) would be just an empty stub. However, given the complexity of many applications, this is generally too restrictive. A good rule of thumb for **DllMain** is to postpone as much initialization as possible. Lazy initialization increases robustness of the application because this initialization is not performed while the loader lock is held. Also, lazy initialization enables you to safely use much more of the Windows API. 12 | desktop-src/Dlls/dynamic-link-library-best-practices.md:40:- Acquire a synchronization object that is owned by code that is waiting to acquire the loader lock. This can cause a deadlock. 13 | desktop-src/Dlls/dynamic-link-library-best-practices.md:44:- Call [**ExitThread**](/windows/win32/api/libloaderapi/nf-libloaderapi-freelibraryandexitthread). Exiting a thread during DLL detach can cause the loader lock to be acquired again, causing a deadlock or a crash. 14 | desktop-src/Dlls/dynamic-link-library-best-practices.md:66:It is important to note that the loader calls [**DllMain**](dllmain.md) with the loader lock already acquired, so the loader lock should have the highest precedence in the locking hierarchy. Also note that code only has to acquire the locks it requires for proper synchronization; it does not have to acquire every single lock that is defined in the hierarchy. For example, if a section of code requires only locks A and C for proper synchronization, then the code should acquire lock A before it acquires lock C; it is not necessary for the code to also acquire lock B. Furthermore, DLL code cannot explicitly acquire the loader lock. If the code must call an API such as [**GetModuleFileName**](/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamea) that can indirectly acquire the loader lock and the code must also acquire a private lock, then the code should call **GetModuleFileName** before it acquires lock P, thus ensuring that load order is respected. 15 | desktop-src/Dlls/dynamic-link-library-best-practices.md:68:Figure 2 is an example that illustrates lock order inversion. Consider a DLL whose main thread contains [**DllMain**](dllmain.md). The library loader acquires the loader lock L and then calls into **DllMain**. The main thread creates synchronization objects A, B, and G to serialize access to its data structures and then tries to acquire lock G. A worker thread that has already successfully acquired lock G then calls a function such as GetModuleHandle that attempts to acquire the loader lock L. Thus, the worker thread is blocked on L and the main thread is blocked on G, resulting in a deadlock. 16 | desktop-src/Dlls/dynamic-link-library-best-practices.md:87:- Thread synchronization is tricky because waiting on threads to exit in [**DllMain**](dllmain.md) can cause a deadlock. For example, DLL A holds the loader lock. It signals thread T to exit and waits for the thread to exit. Thread T exits and the loader tries to acquire the loader lock to call into DLL A’s **DllMain** with DLL\_THREAD\_DETACH. This causes a deadlock. To minimize the risk of a deadlock: 17 | desktop-src/Dlls/dynamic-link-library-best-practices.md:92:If a DLL is unloaded after all its threads have been created, but before they begin executing, the threads may crash. If the DLL created threads in its **DllMain** as part of its initialization, some threads may not have finished initialization and their DLL\_THREAD\_ATTACH message is still waiting to be delivered to the DLL. In this situation, if the DLL is unloaded, it will begin terminating threads. However, some threads may be blocked behind the loader lock. Their DLL\_THREAD\_ATTACH messages are processed after the DLL has been unmapped, causing the process to crash. 18 | desktop-src/Dlls/dynamic-link-library-best-practices.md:99:- If using a private lock inside [**DllMain**](dllmain.md), define a locking hierarchy and use it consistently. The loader lock must be at the bottom of this hierarchy. 19 | -------------------------------------------------------------------------------- /data/windows/timeline-verification/README.md: -------------------------------------------------------------------------------- 1 | # Timeline Verification 2 | 3 | Writing about history requires timeline information to get an accurate depiction of events. Let's collect some basic details on the files included throughout Windows versions to help establish timeline information on its development. 4 | 5 | ## Methods 6 | 7 | ### DLL Imports/Exports 8 | 9 | Contained are the imports and/or exports of: 10 | 11 | - Windows 3.1 (released 1992) `KRNL286.EXE`/`KRNL386.EXE` (16-bit and 32-bit core Windows DLLs) 12 | - Windows NT 3.1 (released 1993 as the first Windows NT version) `NTDLL.dll` and `KERNEL32.dll` 13 | 14 | We get the files from the [v86 Windows 3.1 machine](https://copy.sh/v86/?profile=windows31) (click "Get hard disk image", not all machines have this button) and from this [Windows NT 3.1 VirtualBox image](https://archive.org/details/windows-nt-3.1_202208). For the latter, convert the VDI file to a hard disk image with `qemu-img` (`qemu-img convert -f vdi -O raw 'Windows NT 3.1.vdi' 'Windows NT 3.1.img'`) then mount the image to extract files. We use the `exehdr` tool to get MS-DOS EXE information (obtained by installing an [early version of the VC/VC++ development tools](https://winworldpc.com/product/visual-c/1x)) and the `dumpbin` tool to get DLL information (obtained by installing Visual Studio with the C++ development pack). 15 | 16 | Note that MS-DOS EXE pseudo-DLLs did not support imports because Windows 3.1 did not have a dynamic linking mechanism. An EXE had to manually load another EXE, for instance by calling `LoadLibrary`, to use its functionality. 17 | -------------------------------------------------------------------------------- /data/windows/timeline-verification/dll-imports-exports/windows-31/KRNL286-exports.txt: -------------------------------------------------------------------------------- 1 | 2 | Microsoft (R) EXE File Header Utility Version 3.20 3 | Copyright (C) Microsoft Corp 1985-1993. All rights reserved. 4 | 5 | Library: KERNEL 6 | Description: Microsoft Windows Kernel Interface Version 3.10 7 | Data: SHARED 8 | Initialization: Global 9 | Initial CS:IP: seg 1 offset ffff99cb 10 | Initial SS:SP: seg 0 offset 0000 11 | DGROUP: seg 4 12 | Heap allocation: 0200 bytes 13 | Runs in protected mode only 14 | 15 | no. type address file mem flags 16 | 1 CODE 00001620 0b6cb 0b6cb PRELOAD 17 | 2 CODE 0000cd10 02aac 02aac PRELOAD, (movable), (discardable) 18 | 3 CODE 0000f7e0 00c52 00c54 PRELOAD, (movable), (discardable) 19 | 4 DATA 00010450 013e2 013e2 SHARED, PRELOAD 20 | 21 | 22 | Exports: 23 | ord seg offset name 24 | 342 1 97d0 __GP exported, shared data 25 | 173 254 f000 __ROMBIOS exported 26 | 19 1 02f1 GLOBALUNLOCK exported 27 | 184 1 12ca GLOBALDOSALLOC exported 28 | 127 1 06fd GETPRIVATEPROFILEINT exported, shared data 29 | 166 2 028f WINEXEC exported 30 | 122 1 79f3 ISTASKLOCKED exported 31 | 99 1 0beb GETLPERRMODE exported 32 | 88 1 7b41 LSTRCPY exported 33 | 81 1 0494 _LCLOSE exported 34 | 335 1 4423 ISBADWRITEPTR exported 35 | 171 1 00e4 ALLOCDSTOCSALIAS exported 36 | 170 1 00d0 ALLOCCSTODSALIAS exported 37 | 137 1 0211 FATALAPPEXIT exported 38 | 183 254 0000 __0000H exported 39 | 18 1 028f GLOBALLOCK exported 40 | 161 2 29bd LOCALCOUNTFREE exported 41 | 193 254 0040 __0040H exported 42 | 42 1 943b DISABLEDOS exported 43 | 198 1 03c5 GLOBALUNFIX exported 44 | 120 1 6b6e UNDEFDYNLINK exported 45 | 159 1 0ec1 GLOBALHANDLENORIP exported 46 | 85 1 0469 _LOPEN exported 47 | 141 1 7859 INITTASK1 exported 48 | 164 1 034a GLOBALLRUNEWEST exported 49 | 130 3 0888 FILECDR exported 50 | 55 3 08ee CATCH exported 51 | 26 1 0e40 GLOBALFREEALL exported 52 | 128 1 072f GETPRIVATEPROFILESTRING exported, shared data 53 | 339 1 96be DIAGQUERY exported 54 | 77 1 7b9f RESERVED1 exported 55 | 78 1 7b9c RESERVED2 exported 56 | 101 1 1bdd NOHOOKDOSCALL exported 57 | 79 1 7b96 RESERVED3 exported 58 | 83 1 04b3 _LCREAT exported 59 | 110 1 7412 PATCHCODEHANDLE exported 60 | 80 1 7b99 RESERVED4 exported 61 | 87 1 7b60 RESERVED5 exported 62 | 53 1 41c7 CALLPROCINSTANCE exported 63 | 341 3 084c TOOLHELPHOOK exported 64 | 320 1 7695 ISTASK exported 65 | 318 1 964b FATALEXITHOOK exported 66 | 126 1 2b47 MEMORYFREED exported 67 | 165 1 9588 A20PROC exported 68 | 51 3 00a8 MAKEPROCINSTANCE exported 69 | 107 3 0132 SETERRORMODE exported 70 | 343 2 0689 REGISTERWINOLDAPHOOK exported 71 | 204 3 014e SWAPRECORDING exported 72 | 158 1 7683 ISWINOLDAPTASK exported 73 | 84 1 04de _LLSEEK exported 74 | 33 1 79d9 LOCKCURRENTTASK exported 75 | 319 1 4b9c FLUSHCACHEDFILEHANDLE exported 76 | 316 1 1247 GETFREEMEMINFO exported 77 | 93 1 419a GETCODEHANDLE exported 78 | 52 3 00c5 FREEPROCINSTANCE exported 79 | 152 3 08e2 GETNUMTASKS exported 80 | 7 1 0585 LOCALFREE exported 81 | 10 1 05cf LOCALSIZE exported 82 | 192 1 039d GLOBALPAGEUNLOCK exported 83 | 346 1 4445 ISBADHUGEREADPTR exported 84 | 163 1 035e GLOBALLRUOLDEST exported 85 | 25 1 1100 GLOBALCOMPACT exported 86 | 65 1 019f SIZEOFRESOURCE exported 87 | 8 1 0599 LOCALLOCK exported 88 | 121 1 8582 LOCALSHRINK exported 89 | 105 3 07d9 GETEXEVERSION exported 90 | 124 1 943c ENABLEKERNEL exported 91 | 13 1 8557 LOCALCOMPACT exported 92 | 2 1 951b EXITKERNEL exported 93 | 311 1 962b GETSETKERNELDOSPROC exported 94 | 162 2 29f5 LOCALHEAPSIZE exported 95 | 76 3 04b6 DELETEPATHNAME exported 96 | 4 2 28ab LOCALINIT exported 97 | 131 3 08ce GETDOSENVIRONMENT exported 98 | 30 1 750e WAITEVENT exported 99 | 117 1 7589 OLDYIELD exported 100 | 100 2 2aab VALIDATECODESEGMENTS exported 101 | 36 1 7bfe GETCURRENTTASK exported 102 | 134 3 0054 GETWINDOWSDIRECTORY exported 103 | 129 1 07bb WRITEPRIVATEPROFILESTRING exported, shared data 104 | 328 1 8ca0 _DEBUGOUTPUT exported 105 | 177 1 010c PRESTOCHANGOSELECTOR exported 106 | 104 1 0401 GETCODEINFO exported 107 | 95 2 022d LOADLIBRARY exported 108 | 197 1 03b1 GLOBALFIX exported 109 | 191 1 0389 GLOBALPAGELOCK exported 110 | 403 1 26a9 K403 exported 111 | 20 1 02dd GLOBALSIZE exported 112 | 404 1 267f K404 exported 113 | 98 1 54b7 GETLASTDISKCHANGE exported 114 | 340 1 96cd DIAGOUTPUT exported 115 | 9 1 05e3 LOCALUNLOCK exported 116 | 23 1 03d9 LOCKSEGMENT exported 117 | 200 1 3848 VALIDATEFREESPACES exported 118 | 35 1 75e2 GETTASKQUEUE exported 119 | 338 1 4512 HASGPHANDLER exported 120 | 22 1 030e GLOBALFLAGS exported 121 | 169 1 042a GETFREESPACE exported 122 | 59 1 0789 WRITEPROFILESTRING exported, shared data 123 | 188 1 2508 GETSELECTORLIMIT exported 124 | 327 1 8c6d K327 exported 125 | 111 1 0322 GLOBALWIRE exported 126 | 102 1 1be3 DOS3CALL exported 127 | 37 3 024e GETCURRENTPDB exported 128 | 348 1 45fc HMEMCPY exported 129 | 180 1 2464 LONGPTRADD exported 130 | 329 1 8c97 K329 exported 131 | 74 1 060b OPENFILE exported 132 | 32 1 7635 SETPRIORITY exported 133 | 202 3 081d REGISTERPTRACE exported 134 | 151 3 07e6 WINOLDAPCALL exported 135 | 112 1 0336 GLOBALUNWIRE exported 136 | 353 1 7af1 LSTRCPYN exported 137 | 56 3 0928 THROW exported 138 | 96 2 018a FREELIBRARY exported 139 | 68 3 041a INITATOMTABLE exported 140 | 34 1 7628 SETTASKQUEUE exported 141 | 138 2 1e49 GETHEAPSPACES exported 142 | 47 2 01bb GETMODULEHANDLE exported 143 | 92 3 0508 GETTEMPDRIVE exported 144 | 354 1 9667 GETAPPCOMPATFLAGS exported 145 | 150 1 754a DIRECTEDYIELD exported 146 | 345 1 45d9 ISSHAREDSELECTOR exported 147 | 189 1 2527 SETSELECTORLIMIT exported 148 | 199 1 066d SETHANDLECOUNT exported 149 | 114 254 0008 __AHINCR exported 150 | 72 1 01cd GETATOMNAME exported 151 | 139 1 59c5 DOSIGNAL exported 152 | 115 1 9145 OUTPUTDEBUGSTRING exported 153 | 28 1 1159 GLOBALMASTERHANDLE exported 154 | 347 1 4485 ISBADHUGEWRITEPTR exported 155 | 310 2 2a00 LOCALHANDLEDELTA exported 156 | 113 254 0003 __AHSHIFT exported 157 | 344 2 06f3 GETWINOLDAPHOOKS exported 158 | 167 1 47a2 GETEXPWINVER exported 159 | 58 1 06af GETPROFILESTRING exported, shared data 160 | 38 1 760a SETTASKSIGNALPROC exported 161 | 326 2 1e9c ISROMFILE exported 162 | 201 1 9284 REPLACEINST exported 163 | 64 1 0140 ACCESSRESOURCE exported 164 | 156 1 3ed2 LIMITEMSPAGES exported 165 | 50 2 019e GETPROCADDRESS exported 166 | 350 1 4691 _HWRITE exported 167 | 174 254 a000 __A000H exported 168 | 90 1 7ac8 LSTRLEN exported 169 | 168 1 7c8a DIRECTRESALLOC exported 170 | 135 3 007e GETSYSTEMDIRECTORY exported 171 | 351 1 572b BUNNY_351 exported 172 | 97 3 00dc GETTEMPFILENAME exported 173 | 132 3 07ab GETWINFLAGS exported 174 | 181 254 b000 __B000H exported 175 | 45 2 0244 LOADMODULE exported 176 | 185 1 1303 GLOBALDOSFREE exported 177 | 203 1 8c40 DEBUGBREAK exported 178 | 337 1 44ee ISBADSTRINGPTR exported 179 | 6 1 05ad LOCALREALLOC exported 180 | 11 1 853c LOCALHANDLE exported 181 | 89 1 7b51 LSTRCAT exported 182 | 21 1 027b GLOBALHANDLE exported 183 | 195 254 c000 __C000H exported 184 | 182 254 b800 __B800H exported 185 | 86 1 053c _LWRITE exported 186 | 5 1 0569 LOCALALLOC exported 187 | 205 1 8f96 CVWBREAK exported 188 | 3 3 07c6 GETVERSION exported 189 | 123 1 3ed8 KBDRST exported 190 | 109 1 1205 SWITCHSTACKBACK exported 191 | 155 1 1168 GETTASKDS exported 192 | 179 254 d000 __D000H exported 193 | 54 2 0219 GETINSTANCEDATA exported 194 | 73 1 01fd GETATOMHANDLE exported 195 | 71 1 01b9 DELETEATOM exported 196 | 125 1 943d DISABLEKERNEL exported 197 | 190 254 e000 __E000H exported 198 | 67 3 002e SETRESOURCEHANDLER exported 199 | 61 1 0126 LOADRESOURCE exported 200 | 355 1 96a6 GETWINDEBUGINFO exported, shared data 201 | 325 1 8cba LOGPARAMERROR exported, shared data 202 | 175 1 1bea ALLOCSELECTOR exported 203 | 91 2 2688 INITTASK exported 204 | 140 3 068c SETSIGHANDLER exported 205 | 136 3 0675 GETDRIVETYPE exported 206 | 194 254 f000 __F000H exported 207 | 57 1 0689 GETPROFILEINT exported, shared data 208 | 46 2 0176 FREEMODULE exported 209 | 69 1 41e2 FINDATOM exported 210 | 106 1 2b7e SETSWAPAREASIZE exported 211 | 14 2 0278 LOCALNOTIFY exported 212 | 332 4 0218 THHOOK exported 213 | 315 1 6925 WRITEOUTPROFILES exported, shared data 214 | 196 1 2555 SELECTORACCESSRIGHTS exported 215 | 133 1 4718 GETEXEPTR exported 216 | 334 1 4402 ISBADREADPTR exported 217 | 324 1 8ca1 LOGERROR exported, shared data 218 | 157 1 3ed9 GETCURPID exported 219 | 62 1 018b LOCKRESOURCE exported 220 | 31 1 75d7 POSTEVENT exported 221 | 16 1 02a3 GLOBALREALLOC exported 222 | 356 1 96b2 SETWINDEBUGINFO exported, shared data 223 | 323 2 1e97 ISROMMODULE exported 224 | 17 1 025e GLOBALFREE exported 225 | 186 1 1f04 GETSELECTORBASE exported 226 | 41 1 943a ENABLEDOS exported 227 | 1 1 8ba0 FATALEXIT exported, shared data 228 | 103 1 9595 NETBIOSCALL exported 229 | 116 2 278b INITLIB exported 230 | 75 3 04be OPENPATHNAME exported 231 | 49 2 01e6 GETMODULEFILENAME exported 232 | 63 1 0177 FREERESOURCE exported 233 | 207 1 7bf7 ISDBCSLEADBYTE exported 234 | 12 1 05f7 LOCALFLAGS exported 235 | 314 1 8fe7 DEBUGDEFINESEGMENT exported 236 | 160 1 98f0 EMSCOPY exported 237 | 70 1 41df ADDATOM exported 238 | 66 1 015d ALLOCRESOURCE exported 239 | 336 1 44c8 ISBADCODEPTR exported 240 | 154 1 0372 GLOBALNOTIFY exported 241 | 349 1 468c _HREAD exported 242 | 176 1 00f8 FREESELECTOR exported 243 | 172 1 1bfb ALLOCALIAS exported 244 | 187 1 24d9 SETSELECTORBASE exported 245 | 24 1 03ed UNLOCKSEGMENT exported 246 | 206 1 1cb8 ALLOCSELECTORARRAY exported 247 | 178 254 0001 __WINFLAGS exported 248 | 108 1 118f SWITCHSTACKTO exported 249 | 15 1 022b GLOBALALLOC exported 250 | 29 1 756c YIELD exported 251 | 48 2 01d2 GETMODULEUSAGE exported 252 | 118 1 75ec GETTASKQUEUEDS exported 253 | 94 3 03ce DEFINEHANDLETABLE exported 254 | 82 1 050c _LREAD exported 255 | 119 1 75fa GETTASKQUEUEES exported 256 | 60 3 0008 FINDRESOURCE exported 257 | 258 | 259 | -------------------------------------------------------------------------------- /data/windows/timeline-verification/dll-imports-exports/windows-31/KRNL386-exports.txt: -------------------------------------------------------------------------------- 1 | 2 | Microsoft (R) EXE File Header Utility Version 3.20 3 | Copyright (C) Microsoft Corp 1985-1993. All rights reserved. 4 | 5 | Library: KERNEL 6 | Description: Microsoft Windows Kernel Interface Version 3.10 7 | Data: SHARED 8 | Initialization: Global 9 | Initial CS:IP: seg 1 offset ffffaa1b 10 | Initial SS:SP: seg 0 offset 0000 11 | DGROUP: seg 4 12 | Heap allocation: 0200 bytes 13 | Runs in protected mode only 14 | 15 | no. type address file mem flags 16 | 1 CODE 00001620 0c571 0c571 PRELOAD 17 | 2 CODE 0000dbc0 02ab8 02ab8 PRELOAD, (movable), (discardable) 18 | 3 CODE 000106a0 00c52 00c54 PRELOAD, (movable), (discardable) 19 | 4 DATA 00011310 013d2 013d2 SHARED, PRELOAD 20 | 21 | 22 | Exports: 23 | ord seg offset name 24 | 342 1 a820 __GP exported, shared data 25 | 173 254 f000 __ROMBIOS exported 26 | 19 1 02cb GLOBALUNLOCK exported 27 | 184 1 139a GLOBALDOSALLOC exported 28 | 127 1 06d7 GETPRIVATEPROFILEINT exported, shared data 29 | 166 2 028f WINEXEC exported 30 | 122 1 81ff ISTASKLOCKED exported 31 | 99 1 0bc5 GETLPERRMODE exported 32 | 88 1 836f LSTRCPY exported 33 | 81 1 046e _LCLOSE exported 34 | 335 1 4b83 ISBADWRITEPTR exported 35 | 171 1 00e4 ALLOCDSTOCSALIAS exported 36 | 170 1 00d0 ALLOCCSTODSALIAS exported 37 | 137 1 0211 FATALAPPEXIT exported 38 | 183 254 0000 __0000H exported 39 | 18 1 0278 GLOBALLOCK exported 40 | 161 2 29c9 LOCALCOUNTFREE exported 41 | 193 254 0040 __0040H exported 42 | 42 1 9c8f DISABLEDOS exported 43 | 198 1 039f GLOBALUNFIX exported 44 | 120 1 72d4 UNDEFDYNLINK exported 45 | 159 1 0ec5 GLOBALHANDLENORIP exported 46 | 85 1 0443 _LOPEN exported 47 | 141 1 8014 INITTASK1 exported 48 | 164 1 0324 GLOBALLRUNEWEST exported 49 | 130 3 0888 FILECDR exported 50 | 55 3 08ee CATCH exported 51 | 26 1 0e39 GLOBALFREEALL exported 52 | 128 1 0709 GETPRIVATEPROFILESTRING exported, shared data 53 | 339 1 a70e DIAGQUERY exported 54 | 77 1 83cd RESERVED1 exported 55 | 78 1 83ca RESERVED2 exported 56 | 101 1 1d92 NOHOOKDOSCALL exported 57 | 79 1 83c4 RESERVED3 exported 58 | 83 1 048d _LCREAT exported 59 | 110 1 7bac PATCHCODEHANDLE exported 60 | 80 1 83c7 RESERVED4 exported 61 | 87 1 838e RESERVED5 exported 62 | 53 1 4927 CALLPROCINSTANCE exported 63 | 341 3 084c TOOLHELPHOOK exported 64 | 320 1 7e50 ISTASK exported 65 | 318 1 9ecf FATALEXITHOOK exported 66 | 126 1 2ea7 MEMORYFREED exported 67 | 165 1 9e0c A20PROC exported 68 | 51 3 00a8 MAKEPROCINSTANCE exported 69 | 107 3 0132 SETERRORMODE exported 70 | 343 2 0689 REGISTERWINOLDAPHOOK exported 71 | 204 3 014e SWAPRECORDING exported 72 | 158 1 7e3e ISWINOLDAPTASK exported 73 | 84 1 04b8 _LLSEEK exported 74 | 33 1 81e5 LOCKCURRENTTASK exported 75 | 319 1 52fc FLUSHCACHEDFILEHANDLE exported 76 | 316 1 127e GETFREEMEMINFO exported 77 | 93 1 48fa GETCODEHANDLE exported 78 | 52 3 00c5 FREEPROCINSTANCE exported 79 | 152 3 08e2 GETNUMTASKS exported 80 | 7 1 055f LOCALFREE exported 81 | 10 1 05a9 LOCALSIZE exported 82 | 192 1 0377 GLOBALPAGEUNLOCK exported 83 | 346 1 4ba5 ISBADHUGEREADPTR exported 84 | 163 1 0338 GLOBALLRUOLDEST exported 85 | 25 1 1105 GLOBALCOMPACT exported 86 | 65 1 019f SIZEOFRESOURCE exported 87 | 8 1 0573 LOCALLOCK exported 88 | 121 1 8db0 LOCALSHRINK exported 89 | 105 3 07d9 GETEXEVERSION exported 90 | 124 1 9c90 ENABLEKERNEL exported 91 | 13 1 8d85 LOCALCOMPACT exported 92 | 2 1 9d6f EXITKERNEL exported 93 | 311 1 9eaf GETSETKERNELDOSPROC exported 94 | 162 2 2a01 LOCALHEAPSIZE exported 95 | 76 3 04b6 DELETEPATHNAME exported 96 | 4 2 28b7 LOCALINIT exported 97 | 131 3 08ce GETDOSENVIRONMENT exported 98 | 30 1 7ca8 WAITEVENT exported 99 | 117 1 7d3e OLDYIELD exported 100 | 100 2 2ab7 VALIDATECODESEGMENTS exported 101 | 36 1 842c GETCURRENTTASK exported 102 | 134 3 0054 GETWINDOWSDIRECTORY exported 103 | 129 1 0795 WRITEPRIVATEPROFILESTRING exported, shared data 104 | 328 1 94ce _DEBUGOUTPUT exported 105 | 177 1 010c PRESTOCHANGOSELECTOR exported 106 | 104 1 03db GETCODEINFO exported 107 | 95 2 022d LOADLIBRARY exported 108 | 197 1 038b GLOBALFIX exported 109 | 191 1 0363 GLOBALPAGELOCK exported 110 | 403 1 29f1 K403 exported 111 | 20 1 02b7 GLOBALSIZE exported 112 | 404 1 29ae K404 exported 113 | 98 1 5c17 GETLASTDISKCHANGE exported 114 | 340 1 a71d DIAGOUTPUT exported 115 | 9 1 05bd LOCALUNLOCK exported 116 | 23 1 03b3 LOCKSEGMENT exported 117 | 200 1 3e4e VALIDATEFREESPACES exported 118 | 35 1 7d9d GETTASKQUEUE exported 119 | 338 1 4c72 HASGPHANDLER exported 120 | 22 1 02e8 GLOBALFLAGS exported 121 | 169 1 0404 GETFREESPACE exported 122 | 59 1 0763 WRITEPROFILESTRING exported, shared data 123 | 188 1 2755 GETSELECTORLIMIT exported 124 | 327 1 949b K327 exported 125 | 111 1 02fc GLOBALWIRE exported 126 | 102 1 1d98 DOS3CALL exported 127 | 37 3 024e GETCURRENTPDB exported 128 | 348 1 4d5c HMEMCPY exported 129 | 180 1 26b9 LONGPTRADD exported 130 | 329 1 94c5 K329 exported 131 | 74 1 05e5 OPENFILE exported 132 | 32 1 7df0 SETPRIORITY exported 133 | 202 3 081d REGISTERPTRACE exported 134 | 151 3 07e6 WINOLDAPCALL exported 135 | 112 1 0310 GLOBALUNWIRE exported 136 | 353 1 831f LSTRCPYN exported 137 | 56 3 0928 THROW exported 138 | 96 2 018a FREELIBRARY exported 139 | 68 3 041a INITATOMTABLE exported 140 | 34 1 7de3 SETTASKQUEUE exported 141 | 138 2 1e4d GETHEAPSPACES exported 142 | 47 2 01bb GETMODULEHANDLE exported 143 | 92 3 0508 GETTEMPDRIVE exported 144 | 354 1 9eeb GETAPPCOMPATFLAGS exported 145 | 150 1 7cff DIRECTEDYIELD exported 146 | 345 1 4d39 ISSHAREDSELECTOR exported 147 | 189 1 276d SETSELECTORLIMIT exported 148 | 199 1 0647 SETHANDLECOUNT exported 149 | 114 254 0008 __AHINCR exported 150 | 72 1 01cd GETATOMNAME exported 151 | 139 1 612c DOSIGNAL exported 152 | 115 1 999b OUTPUTDEBUGSTRING exported 153 | 28 1 1190 GLOBALMASTERHANDLE exported 154 | 347 1 4be5 ISBADHUGEWRITEPTR exported 155 | 310 2 2a0c LOCALHANDLEDELTA exported 156 | 113 254 0003 __AHSHIFT exported 157 | 344 2 06f3 GETWINOLDAPHOOKS exported 158 | 167 1 4f02 GETEXPWINVER exported 159 | 58 1 0689 GETPROFILESTRING exported, shared data 160 | 38 1 7dc5 SETTASKSIGNALPROC exported 161 | 326 2 1ea0 ISROMFILE exported 162 | 201 1 9ada REPLACEINST exported 163 | 64 1 0140 ACCESSRESOURCE exported 164 | 156 1 4632 LIMITEMSPAGES exported 165 | 50 2 019e GETPROCADDRESS exported 166 | 350 1 4df1 _HWRITE exported 167 | 174 254 a000 __A000H exported 168 | 90 1 82f6 LSTRLEN exported 169 | 168 1 84b8 DIRECTRESALLOC exported 170 | 135 3 007e GETSYSTEMDIRECTORY exported 171 | 351 1 5e92 BUNNY_351 exported 172 | 97 3 00dc GETTEMPFILENAME exported 173 | 132 3 07ab GETWINFLAGS exported 174 | 181 254 b000 __B000H exported 175 | 45 2 0244 LOADMODULE exported 176 | 185 1 13d4 GLOBALDOSFREE exported 177 | 203 1 946e DEBUGBREAK exported 178 | 337 1 4c4e ISBADSTRINGPTR exported 179 | 6 1 0587 LOCALREALLOC exported 180 | 11 1 8d6a LOCALHANDLE exported 181 | 89 1 837f LSTRCAT exported 182 | 21 1 0264 GLOBALHANDLE exported 183 | 195 254 c000 __C000H exported 184 | 182 254 b800 __B800H exported 185 | 86 1 0516 _LWRITE exported 186 | 5 1 0543 LOCALALLOC exported 187 | 205 1 97e0 CVWBREAK exported 188 | 3 3 07c6 GETVERSION exported 189 | 123 1 4638 KBDRST exported 190 | 109 1 123c SWITCHSTACKBACK exported 191 | 155 1 119f GETTASKDS exported 192 | 179 254 d000 __D000H exported 193 | 54 2 0219 GETINSTANCEDATA exported 194 | 73 1 01fd GETATOMHANDLE exported 195 | 71 1 01b9 DELETEATOM exported 196 | 125 1 9c91 DISABLEKERNEL exported 197 | 190 254 e000 __E000H exported 198 | 67 3 002e SETRESOURCEHANDLER exported 199 | 61 1 0126 LOADRESOURCE exported 200 | 355 1 9f2a GETWINDEBUGINFO exported, shared data 201 | 325 1 94f2 LOGPARAMERROR exported, shared data 202 | 175 1 1d9e ALLOCSELECTOR exported 203 | 91 2 268d INITTASK exported 204 | 140 3 068c SETSIGHANDLER exported 205 | 136 3 0675 GETDRIVETYPE exported 206 | 194 254 f000 __F000H exported 207 | 57 1 0663 GETPROFILEINT exported, shared data 208 | 46 2 0176 FREEMODULE exported 209 | 69 1 4942 FINDATOM exported 210 | 106 1 2ede SETSWAPAREASIZE exported 211 | 14 2 0278 LOCALNOTIFY exported 212 | 332 4 0218 THHOOK exported 213 | 315 1 708b WRITEOUTPROFILES exported, shared data 214 | 196 1 279b SELECTORACCESSRIGHTS exported 215 | 133 1 4e78 GETEXEPTR exported 216 | 334 1 4b62 ISBADREADPTR exported 217 | 324 1 94cf LOGERROR exported, shared data 218 | 157 1 4639 GETCURPID exported 219 | 62 1 018b LOCKRESOURCE exported 220 | 31 1 7d92 POSTEVENT exported 221 | 16 1 028c GLOBALREALLOC exported 222 | 356 1 9f36 SETWINDEBUGINFO exported, shared data 223 | 323 2 1e9b ISROMMODULE exported 224 | 17 1 0247 GLOBALFREE exported 225 | 186 1 2252 GETSELECTORBASE exported 226 | 41 1 9c8e ENABLEDOS exported 227 | 1 1 93ce FATALEXIT exported, shared data 228 | 103 1 9e19 NETBIOSCALL exported 229 | 116 2 2797 INITLIB exported 230 | 75 3 04be OPENPATHNAME exported 231 | 49 2 01e6 GETMODULEFILENAME exported 232 | 63 1 0177 FREERESOURCE exported 233 | 207 1 8425 ISDBCSLEADBYTE exported 234 | 12 1 05d1 LOCALFLAGS exported 235 | 314 1 9835 DEBUGDEFINESEGMENT exported 236 | 160 1 a940 EMSCOPY exported 237 | 70 1 493f ADDATOM exported 238 | 66 1 015d ALLOCRESOURCE exported 239 | 336 1 4c28 ISBADCODEPTR exported 240 | 154 1 034c GLOBALNOTIFY exported 241 | 349 1 4dec _HREAD exported 242 | 176 1 00f8 FREESELECTOR exported 243 | 172 1 21fb ALLOCALIAS exported 244 | 187 1 2725 SETSELECTORBASE exported 245 | 24 1 03c7 UNLOCKSEGMENT exported 246 | 206 1 1df5 ALLOCSELECTORARRAY exported 247 | 178 254 0001 __WINFLAGS exported 248 | 108 1 11c6 SWITCHSTACKTO exported 249 | 15 1 022b GLOBALALLOC exported 250 | 29 1 7d21 YIELD exported 251 | 48 2 01d2 GETMODULEUSAGE exported 252 | 118 1 7da7 GETTASKQUEUEDS exported 253 | 94 3 03ce DEFINEHANDLETABLE exported 254 | 82 1 04e6 _LREAD exported 255 | 119 1 7db5 GETTASKQUEUEES exported 256 | 60 3 0008 FINDRESOURCE exported 257 | 258 | 259 | -------------------------------------------------------------------------------- /data/windows/timeline-verification/dll-imports-exports/windows-nt-31/KERNEL32-imports.txt: -------------------------------------------------------------------------------- 1 | Microsoft (R) COFF/PE Dumper Version 14.30.30706.0 2 | Copyright (C) Microsoft Corporation. All rights reserved. 3 | 4 | 5 | Dump of file KERNEL32.DLL 6 | 7 | File Type: DLL 8 | 9 | Section contains the following imports: 10 | 11 | ntdll.dll 12 | 778C23C0 Import Address Table 13 | 778C2028 Import Name Table 14 | 2C4F15D7 time date stamp Thu Jul 22 18:33:59 1993 15 | FFFFFFFF Index of first forwarder reference 16 | 17 | 771A3E90 0 CsrAllocateCaptureBuffer 18 | 771A3F60 2 CsrAllocateMessagePointer 19 | 771A3FE0 3 CsrCaptureMessageBuffer 20 | 771A4020 4 CsrCaptureMessageString 21 | 771A3CF0 6 CsrClientCallServer 22 | 771A3090 7 CsrClientConnectToServer 23 | 771A3F40 C CsrFreeCaptureBuffer 24 | 771A36F0 D CsrIdentifyAlertableThread 25 | 771A36E0 E CsrNewThread 26 | 771A3730 11 CsrSetPriorityClass 27 | 771A3770 12 CsrStartProfile 28 | 771A3800 13 CsrStopDumpProfile 29 | 771ACCA0 16 DbgBreakPoint 30 | 771ACCC0 17 DbgPrint 31 | 771A4780 1B DbgUiConnectToDbg 32 | 771A48F0 1C DbgUiContinue 33 | 771A4860 1D DbgUiWaitStateChange 34 | 771ACE90 21 LdrAccessResource 35 | 771AD010 23 LdrFindResourceDirectory_U 36 | 771ACFF0 24 LdrFindResource_U 37 | 771A7F30 25 LdrGetDllHandle 38 | 771A8550 26 LdrGetProcedureAddress 39 | 771A7AF0 28 LdrLoadDll 40 | 771A57C0 2A LdrQueryImageFileExecutionOptions 41 | 771A8830 2B LdrQueryProcessModuleInformation 42 | 771A5470 2C LdrShutdownProcess 43 | 771A5570 2D LdrShutdownThread 44 | 771A8140 2E LdrUnloadDll 45 | 771AC1D0 35 NtAdjustPrivilegesToken 46 | 771AC210 39 NtAllocateVirtualMemory 47 | 771AC240 3C NtClose 48 | 771AC280 40 NtContinue 49 | 771AC290 41 NtCreateDirectoryObject 50 | 771AC2A0 42 NtCreateEvent 51 | 771AC2C0 44 NtCreateFile 52 | 771AC2D0 45 NtCreateKey 53 | 771AC2E0 46 NtCreateMailslotFile 54 | 771AC2F0 47 NtCreateMutant 55 | 771AC300 48 NtCreateNamedPipeFile 56 | 771AC330 4B NtCreateProcess 57 | 771AC350 4D NtCreateSection 58 | 771AC360 4E NtCreateSemaphore 59 | 771AC380 50 NtCreateThread 60 | 771AC3B0 54 NtDelayExecution 61 | 771AC3D0 56 NtDeleteValueKey 62 | 771AC3E0 57 NtDeviceIoControlFile 63 | 771AC400 59 NtDuplicateObject 64 | 771AC420 5B NtEnumerateKey 65 | 771AC430 5C NtEnumerateValueKey 66 | 771AC450 5E NtFlushBuffersFile 67 | 771AC460 5F NtFlushInstructionCache 68 | 771AC470 60 NtFlushKey 69 | 771AC480 61 NtFlushVirtualMemory 70 | 771AC49C 63 NtFreeVirtualMemory 71 | 771AC4AC 64 NtFsControlFile 72 | 771AC4BC 65 NtGetContextThread 73 | 771AC4CC 66 NtGetTickCount 74 | 771AC540 6E NtLockFile 75 | 771AC550 6F NtLockVirtualMemory 76 | 771AC570 71 NtMapViewOfSection 77 | 771AC580 72 NtNotifyChangeDirectoryFile 78 | 771AC5A0 74 NtOpenDirectoryObject 79 | 771AC5B0 75 NtOpenEvent 80 | 771AC5D0 77 NtOpenFile 81 | 771AC5E0 78 NtOpenKey 82 | 771AC5F0 79 NtOpenMutant 83 | 771AC610 7B NtOpenProcess 84 | 771AC620 7C NtOpenProcessToken 85 | 771AC630 7D NtOpenSection 86 | 771AC640 7E NtOpenSemaphore 87 | 771AC650 7F NtOpenSymbolicLinkObject 88 | 771AC660 80 NtOpenThread 89 | 771AC670 81 NtOpenThreadToken 90 | 771AC6C0 86 NtProtectVirtualMemory 91 | 771AC6D0 87 NtPulseEvent 92 | 771AC6E0 88 NtQueryDefaultLocale 93 | 771AC6F0 89 NtQueryDirectoryFile 94 | 771AC700 8A NtQueryDirectoryObject 95 | 771AC710 8B NtQueryEaFile 96 | 771AC720 8C NtQueryEvent 97 | 771AC730 8D NtQueryInformationFile 98 | 771AC750 8F NtQueryInformationProcess 99 | 771AC760 90 NtQueryInformationThread 100 | 771AC7C0 96 NtQueryPerformanceCounter 101 | 771AC7D0 97 NtQuerySection 102 | 771AC7E0 98 NtQuerySecurityObject 103 | 771AC800 9A NtQuerySymbolicLinkObject 104 | 771AC820 9C NtQuerySystemInformation 105 | 771AC830 9D NtQuerySystemTime 106 | 771AC850 9F NtQueryValueKey 107 | 771AC860 A0 NtQueryVirtualMemory 108 | 771AC870 A1 NtQueryVolumeInformationFile 109 | 771AC890 A3 NtRaiseHardError 110 | 771AC8A0 A4 NtReadFile 111 | 771AC8C0 A6 NtReadVirtualMemory 112 | 771AC8E0 A8 NtReleaseMutant 113 | 771AC8F0 A9 NtReleaseProcessMutant 114 | 771AC8FC AA NtReleaseSemaphore 115 | 771AC97C B2 NtResetEvent 116 | 771AC99C B4 NtResumeThread 117 | 771AC9BC B6 NtSetContextThread 118 | 771AC9EC B9 NtSetEaFile 119 | 771AC9FC BA NtSetEvent 120 | 771ACA30 BE NtSetInformationFile 121 | 771ACA50 C0 NtSetInformationProcess 122 | 771ACA60 C1 NtSetInformationThread 123 | 771ACAC4 C8 NtSetSecurityObject 124 | 771ACAE4 CA NtSetSystemTime 125 | 771ACB04 CC NtSetValueKey 126 | 771ACB14 CD NtSetVolumeInformationFile 127 | 771ACB54 D1 NtSuspendThread 128 | 771ACB74 D3 NtTerminateProcess 129 | 771ACB84 D4 NtTerminateThread 130 | 771ACBC0 D8 NtUnlockFile 131 | 771ACBD0 D9 NtUnlockVirtualMemory 132 | 771ACBE0 DA NtUnmapViewOfSection 133 | 771ACC0C DD NtWaitForMultipleObjects 134 | 771ACC2C DE NtWaitForProcessMutant 135 | 771ACC1C DF NtWaitForSingleObject 136 | 771ACC58 E2 NtWriteFile 137 | 771ACC78 E4 NtWriteVirtualMemory 138 | 771B23F0 EC RtlAcquirePebLock 139 | 771B2850 EF RtlAddAccessAllowedAce 140 | 771B5700 F7 RtlAllocateHeap 141 | 771A9520 F8 RtlAnalyzeProfile 142 | 771B7D80 F9 RtlAnsiCharToUnicodeChar 143 | 771B8670 FA RtlAnsiStringToUnicodeSize 144 | 771B7CB0 FB RtlAnsiStringToUnicodeString 145 | 771B8FF0 FC RtlAppendAsciizToString 146 | 771B9060 FD RtlAppendStringToString 147 | 771B8A90 FE RtlAppendUnicodeStringToString 148 | 771B8A00 FF RtlAppendUnicodeToString 149 | 771BA170 105 RtlAreBitsSet 150 | 771BA4A0 108 RtlCharToInteger 151 | 771B98A0 10B RtlClearBits 152 | 771B70F0 10C RtlCompactHeap 153 | 771C8260 10D RtlCompareMemory 154 | 771C8240 113 RtlConvertLongToLargeInteger 155 | 771B8990 11E RtlCopyUnicodeString 156 | 771B2430 11F RtlCreateAcl 157 | 771B4900 122 RtlCreateHeap 158 | 771BC610 123 RtlCreateProcessParameters 159 | 771B3430 125 RtlCreateSecurityDescriptor 160 | 771AA3A0 12F RtlDeleteCriticalSection 161 | 771B4DE0 135 RtlDestroyHeap 162 | 771BC990 136 RtlDestroyProcessParameters 163 | 771A15B0 137 RtlDetermineDosPathNameType_U 164 | 771A2D90 138 RtlDoesFileExists_U 165 | 771A2820 139 RtlDosPathNameToNtPathName_U 166 | 771C8014 13C RtlEnlargedIntegerMultiply 167 | 771C8020 13E RtlEnlargedUnsignedMultiply 168 | 771AC0D0 13F RtlEnterCriticalSection 169 | 771B8810 148 RtlEqualUnicodeString 170 | 771BB300 14A RtlExpandEnvironmentStrings 171 | 771BB460 14B RtlExpandEnvironmentStrings_U 172 | 771C804C 14D RtlExtendedLargeIntegerDivide 173 | 771B9840 152 RtlFindClearBitsAndSet 174 | 771BDE60 155 RtlFindMessage 175 | 771BDF50 159 RtlFormatMessage 176 | 771B85D0 15A RtlFreeAnsiString 177 | 771B6B40 15B RtlFreeHeap 178 | 771B85F0 15C RtlFreeOemString 179 | 771B85B0 15E RtlFreeUnicodeString 180 | 771A11A0 163 RtlGetCurrentDirectory_U 181 | 771B3810 164 RtlGetDaclSecurityDescriptor 182 | 771A27E0 166 RtlGetFullPathName_U 183 | 771B6EF0 168 RtlGetHandleValueHeap 184 | 771BB520 16A RtlGetNtGlobalFlags 185 | 771AD8D0 16F RtlImageDirectoryEntryToData 186 | 771AD840 170 RtlImageNtHeader 187 | 771B8C30 172 RtlInitAnsiString 188 | 771B8BF0 175 RtlInitString 189 | 771B8C70 176 RtlInitUnicodeString 190 | 771AA230 179 RtlInitializeCriticalSection 191 | 771A8970 17B RtlInitializeProfile 192 | 771B3020 17E RtlInitializeSid 193 | 771BA330 181 RtlIntegerToChar 194 | 771BA810 182 RtlIntegerToUnicodeString 195 | 771A1950 183 RtlIsDosDeviceName_U 196 | 771C8000 185 RtlLargeIntegerAdd 197 | 771C8218 188 RtlLargeIntegerNegate 198 | 771C822C 18B RtlLargeIntegerSubtract 199 | 771AC130 18D RtlLeaveCriticalSection 200 | 771B2F20 18E RtlLengthRequiredSid 201 | 771B35B0 18F RtlLengthSecurityDescriptor 202 | 771BFE10 191 RtlLocalTimeToSystemTime 203 | 771B4D80 192 RtlLockHeap 204 | 771C00D0 195 RtlLookupSymbolByAddress 205 | 771C02E0 1A0 RtlNtStatusToDosError 206 | 771B86A0 1A4 RtlOemStringToUnicodeSize 207 | 771B7F50 1A5 RtlOemStringToUnicodeString 208 | 771BB610 1A7 RtlOpenCurrentUser 209 | 771B8E50 1A9 RtlPrefixString 210 | 771B8F10 1AA RtlPrefixUnicodeString 211 | 771BBCE0 1AB RtlQueryEnvironmentVariable 212 | 771BC4D0 1AC RtlQueryEnvironmentVariable_U 213 | 771BEE00 1AF RtlQueryProcessBackTraceInformation 214 | 771B7230 1B0 RtlQueryProcessHeapInformation 215 | 771AA680 1B1 RtlQueryProcessLockInformation 216 | 771BAEF0 1B2 RtlQueryRegistryValues 217 | 771BB770 1B4 RtlQueryTimeZoneInformation 218 | 771C0750 1B5 RtlRaiseException 219 | 771C0800 1B6 RtlRaiseStatus 220 | 771B6500 1B8 RtlReAllocateHeap 221 | 771B2410 1BB RtlReleasePebLock 222 | 771A12A0 1C6 RtlSetCurrentDirectory_U 223 | 771B3790 1C8 RtlSetDaclSecurityDescriptor 224 | 771BBE80 1C9 RtlSetEnvironmentVariable 225 | 771B6E20 1CB RtlSetHandleValueHeap 226 | 771BB8C0 1D1 RtlSetTimeZoneInformation 227 | 771B6FC0 1D2 RtlSizeHeap 228 | 771B7320 1D3 RtlSnapShotHeap 229 | 771A9350 1D5 RtlStartProfile 230 | 771A9490 1D7 RtlStopProfile 231 | 771B30A0 1D9 RtlSubAuthoritySid 232 | 771BFDD0 1DC RtlSystemTimeToLocalTime 233 | 771BFA00 1DD RtlTimeFieldsToTime 234 | 771BF670 1E1 RtlTimeToTimeFields 235 | 771B8610 1E2 RtlUnicodeStringToAnsiSize 236 | 771B7DD0 1E3 RtlUnicodeStringToAnsiString 237 | 771BA630 1E5 RtlUnicodeStringToInteger 238 | 771B8640 1E6 RtlUnicodeStringToOemSize 239 | 771B8020 1E7 RtlUnicodeStringToOemString 240 | 771AF080 1E9 RtlUnicodeToMultiByteN 241 | 771B4DB0 1ED RtlUnlockHeap 242 | 771BDCC0 1EE RtlUnwind 243 | 771B8540 1EF RtlUpcaseUnicodeChar 244 | 771AA5D0 206 RtlpUnWaitCriticalSection 245 | 771AA4E0 207 RtlpWaitForCriticalSection 246 | 247 | Summary 248 | 249 | 2000 .bss 250 | 3000 .data 251 | 4000 .edata 252 | 2000 .idata 253 | 1000 .rdata 254 | 4000 .reloc 255 | F000 .rsrc 256 | 38000 .text 257 | 258 | -------------------------------------------------------------------------------- /data/windows/timeline-verification/dll-imports-exports/windows-nt-31/NTDLL-imports.txt: -------------------------------------------------------------------------------- 1 | Microsoft (R) COFF/PE Dumper Version 14.30.30706.0 2 | Copyright (C) Microsoft Corporation. All rights reserved. 3 | 4 | 5 | Dump of file NTDLL.DLL 6 | 7 | File Type: DLL 8 | 9 | Summary 10 | 11 | 2000 .bss 12 | 2000 .data 13 | 6000 .edata 14 | 1000 .rdata 15 | 3000 .reloc 16 | F000 .rsrc 17 | 22000 .text 18 | 5000 ECODE 19 | 1000 EDATA 20 | 1000 _TEXT 21 | 22 | --------------------------------------------------------------------------------