6 days ago Matt said
- Is it possible to write C programs that do not depend on the CRT? What are the limitations?
Yes, but you have to choose not to. In Visual Studio you can go properties -> Linker -> Ignore All Default Libraries -> True. If you do this you'll also need to turn off buffer security checks and runtime checks in the compiler options and if writing a DLL in compiler -> Advanced set the entry point to "DllMain" or if writing an exe change it to "main".
Things that have to live underneath the CRT (e.g. the Windows libraries, drivers and the kernel) need to do this if you want to build them with visual studio, but it's expected that most programs will want the CRT as it provides all sorts of nice low-performance overhead abstractions on top of the Windows functions exported by the Windows API.
- What is exactly the relationship between the Windows API and the CRT? It looks like the CRT is built on top of Win32 (e.g. malloc calls HeapAlloc) but I came across a few Win32 functions that actually rely on the CRT to do their work.
Microsoft's CRT is built on top of the Windows API (Win32). I'm not sure any core Win32 functions rely on the CRT, but do tell me if I'm wrong.
- Is the Windows NT CRT dll (system32\msvcrt.dll) different from the one that ships with Visual Studio? Can we link our applications with this version of the CRT (and no longer ship the CRT dll with our code)?
Yes. The Windows CRT is the one used by the Windows API dlls, e.g. kernel32.dll uses msvcrt.dll, but your dll will use the CRT shipped as part of your version of Visual Studio. The msvcrt.dll in system32 tends to change only with Windows Service Pack.
- I would love to hear more about the way SEH is implemented (what really happens at the cpu and memory level when an exception is thrown, the search for exception handlers, stack unwinding and so forth). The operating system and the compiler are definitely involved but is the CRT also a part of that?
In I386 processors (the x86 and x64 family if you want to call them that) use PAE to reference memory after Windows XP SP1. PAE allows pages of granularity 4096 bytes to be marked as "paged in" or "not available" and can put additional restrictions on them like "not executable", "not writable" or "uncacheable". When your CPU tries an invalid access to a page in memory, the CPU faults to the page fault handler in the kernel.
A lot of the time page faults are "false positives" - you're touching some memory that is paged out (i.e. put in the pagefile to free up physical ram for other processes), part of a memory mapped file that hasn't been read yet and so on, and these page faults are serviced transparently by the kernel so that user-mode won't ever see them, but occasionally the page is marked as "not available" because you're trying to do something invalid like read an unmapped page or execute on a DEP-protected (NX) page.
At this point the kernel SEH handler kicks in and tries to see if the previous mode was kernel mode. If it was, the kernel services the exception (or bluescreens). If the previous mode was user-mode, it queues an APC request to ntdll's KiUserExceptionDispatcher which is responsible for the user-mode part of SEH. ntdll checks if there are any vectored exception handlers that need to be run, then checks for SEH on the stack via the SEH exception chain. It calls each method in turn and if none of the registered exception handlers want to service the exception, ntdll tells the kernel to tear down the process and invoke the DrWatson crash dialog.
The CRT is not typically involved in the SEH chain, but there are exceptions - the CRT does put SEH checks around several of its functions (i.e. the CRT uses SEH) and the compiler emits vectored exception handlers for many "SEH" try catch blocks (because vectored exception handlers are more safe from attack), but it's not really a CRT thing, it's more of an OS/ntdll thing.
- I believe that some runtime floating point support is built inside the CRT but the source code is not available. Can you share with us some implementation details?
The CRT does have code for "upgrading" the floating point performed on the CPU's FPU chip on processors that have low precision by essentially emulating the floating point behaviour in software. This is configurable in the properties of the compiler as either "fast" or "precise" floating point mode.
- Ntdll.dll also contains some C runtime library functions (mostly string related though), are there similar to the ones implemented in the CRT?
Yes and no. NTDLL is the ultimate base of the Windows user-mode - it's the first thing to be run in the process (long before your main function and long before the CRT) and it provides lots of functionality to the other Windows dlls such as kernel32, advapi, user32 and gdi32. The functionality that it provides is basically added on an ad-hoc basis almost like a somewhat manually invoked CRT for the Win32 libraries themselves, but it's not the same as the Visual Studio CRT even if they share code or if the CRT passes down to the NTDLL implementations.
- How is the CRT designed to be thread-safe and do we still need to call functions like _beginthreadex instead of CreateThread to avoid memory leaks?
The CRT is mostly threadsafe, but you'll have to check on a per function basis. In particular the core functions like heap/thread/file/security are all thread safe.
The _beginthreadex/CreateThread is an old issue, and what matters more than which to use is that you pair _beginthreadex's with _endthreadex and CreateThreads with ExitThread. _beginthreadex is better for the CRT (it gives you SEH wrappers for the entire thread for example) and mixing and matching will cause a small memory leak in the _beginthreadex/ExitThread direction and a possible crash in the CreateThread/_endthreadex direction, so choose one and stick to it.