Home > Software engineering >  actual machine code to execute what Win APIs do stays in OS kernel memory space or compiled together
actual machine code to execute what Win APIs do stays in OS kernel memory space or compiled together

Time:05-09

If this question deals with too basic a matter, please forgive me.

As a somewhat-close-to-beginner-level programmer, I really wonder about this--whether the underlying code of every win API function is compiled altogether at the time of writing an app, or whether the machine code for executing win APIs stays in the memory as part of the OS since the pc is booted up, and only the app uses them?

All the APIs for an OS are used by many apps by means of function call. So I thought that rather than making every individual app include the API machine code on their own, apps just contain the header or signature to call the APIs and the API machine code addresses are mapped when launching the app.

I am sorry that I failed to make this question succinct due to my poor English. I really would like to get your insights. Thank you.

CodePudding user response:

The implementation for (most) API calls is provided by the system by way of compiled modules (Portable Executable images). Application code only contains enough information so that the system can identify and load the required modules, and resolve the respective imports.

As an example consider the following code that shows a message box, waits for it to close, and then exits the program:

#include <Windows.h>

int main()
{
    ::MessageBoxW(nullptr, L"Foo", L"Bar", MB_OK);
}

Given the function signature (declared in WinUser.h, which gets pulled in from Windows.h) the compiler can almost generate a call instruction. It knows the number of arguments, their expected types, and the order and location the callee expects them in. What's missing is the actual target address inside user32.dll, that's only known after a process was fully initialized, and had the user32.dll module mapped into its address space.

Clearly, the compiler cannot postpone code generation until after load time. It needs to generate a call instruction now. Since we know that "all problems in computer science can be solved by another level of indirection" that's what the compiler does, too: Instead of emitting a direct call instruction it generates an indirect call. The difference is that, while a direct call immediately needs to provide the target address, an indirect call can specify the address at which the target address is stored.

In x86 assembly, instead of having to say

    call _MessageBoxW@16   ; uh-oh, not yet known

the compiler can conveniently delegate the call to the Import Address Table (IAT):

    call dword ptr [__imp__MessageBoxW@16]

Disaster averted, we've bought us just enough time to fix things up before the code actually executes.

Once a process object is created the system hands over control to its primary thread to finish initialization. Part of that initialization is loading dependencies (such as user32.dll here). Once that has completed, the system finally knows the load address (and ultimately the address of imported symbols, such as _MessageBoxW@16), and can overwrite the IAT entry at address __imp__MessageBoxW@16 with the imported function address.

And that is approximately how the system provides implementations for system services without requiring client applications to know where (physically) they will find them.

I'm saying "approximately" because things are somewhat more involved in reality. If that is something you'll want to learn about, I'll leave it up to Raymond Chen. He has published a series of blog entries covering this topic in far more detail:

  • Related