Hi, I’m Alan Chan and I am a developer on the Visual C++ libraries team. We all know that a library is a collection of functions. However, how does a program actually “access” these functions under the covers?
Essentially, there are 3 different ways that a function can be made available for the program to use: inline, statically linked and dynamically linked.
For “inline functions”, this is just as it sound. The definition and implementation is in the header and is inlined into the user’s code. We provide implementation of the function in textual C++ code format for the user. When they compile with our headers, the compiler will turn the code into the asm instructions and inline them in the user’s code. A prime example of this is STL and marshalling; almost everything is header based.
For “statically linked functions “, we declare the function in our headers, but we ship our implementation in a binary format in the form of static lib (*.lib file) or object file (*.obj files). When the user compiles with our header, it generates a “weak” link to the function symbol. During link time, the linker will search for the symbol in our static lib and find the binary implementation. The linker then takes binary implementation and mixes it with the user’s code into an executable. Therefore, this function exists in the user binary image and doesn’t depend on our DLLs.
For “dynamically linked function”, this is also as what it sounds like. The function exists in the DLL. We only have a declaration in our headers and the definition is in a binary format in the DLL. Similar to the previous type, during compilation, the compiler will generate a weak link to the function symbol. When the program links, it will find the function symbol in the import lib. However, instead of finding the full implementation of the function, it will find a “reference” to the DLL. The linker will generate a list of these imported functions (or better known as import address tables or IAT) in the portable executable (PE) header of the executable. At load time, the Windows loader will read this table, load the DLL, and write the actual memory address of these functions into a table. This step is usually called “binding.” Afterwards, when the executable tries to call an imported function, it will look at this table and jump to address inside the DLL and execute that.