Far Function Prologs

In the discussion of memory management in Chapter 7, I went into great detail about how Windows moves code and data segments in memory and handles multiple instances of programs. Some of that discussion centered on the prolog that the compiler adds to far functions.

When Windows loads a code segment into memory, it alters the function prolog of all exported far functions in the segment. This table shows the results of that process:

Nonexported
Far Function
Exported Function
in a Program
Exported Function
in a Library

PUSH DS NOP MOV AX, xxxx
POP AX NOP  
NOP NOP  
INC BP INC BP INC BP
PUSH BP PUSH BP PUSH BP
MOV BP, SP MOV BP, SP MOV BP, SP
PUSH DS PUSH DS PUSH DS
MOV DS, AX MOV DS, AX MOV DS, AX

These three prologs differ in the way that DS (the data segment address register) is set on entry to the function. In each case, the original value of DS is saved in the function prolog and restored in the function epilog.

The nonexported far function simply sets AX equal to DS and then DS equal to AX. This does nothing.

For an exported function in a program, Windows inserts NOPs in the first 2 bytes of the function. The resultant prolog then sets DS equal to AX. This prolog requires that AX be set to the data segment of the particular instance of the program. By itself, the function is incomplete. You must call MakeProcInstance for these exported functions so that Windows builds an ”instance thunk“ that sets AX equal to the data segment address of the instance. (The only case in which you don't need to call MakeProcInstance for an exported function is for a window procedure specified in a window class structure.)

The exported function in a library is somewhat simpler. Because the library can have only one instance, Windows can simply insert a 3-byte instruction that sets AX equal to the data segment address of the library. Thus, you don't need to use MakeProcInstance with exported far functions in library modules. When a program calls a far function exported from a library module, this prolog sets the data segment equal to the library's data segment. The library function can then use its own data segment. It continues to use the stack segment of the program that called it.