Many peoples would tell me that replacing call with jumps is not a good idea, it will be difficult for debuggers...
But what makes the compiler. It create a call then makes a jump into the table...
It is really a bad idea.
This breaks the stack framing and troubles the Frame-based Exception Handling mechanism (__try __except).
The compiler creates the jump table for a different scope than just creating a list of jumps.
A jump table give a location to link locally in the executable, and in the same time a location that could be dynamically updated when the executable will be linked to a DLL at later time.A standard call to a symbol as:
call dll_func1
Is resolved by the linker replacing the symbol 'dll_func1' with the reference in the jump table that exists at time of static link (the static link is when you run the linker as polink) avoiding the error message for inexistent symbol.
Then at run time the loader will write the memory address of the loaded DLL in the jump table.
The flow will be:
call dll_func1 ; make a regular call to an address that exist in our executable
....
;jump table
dll_func1: jmp ; the helper jump instruction
__imp_dll_func1: 0x00000000 ; this location will be replaced by the real address in the DLL by the loader
The use of declaration specifiers allows to avoid jump and call directly the function in the DLL by dereferencing the location '__imp_dll_func1:
void __declspec(dllimport) dll_func1(void);
...
dll_func1(); // Standar call. See below as the compiler translates it after the specifier dllexport is applied
The code will be:
call [__imp_dll_func1] ; dereferences the address at location __imp_dll_func1 avoiding the jump
In the last case the compiler avoid the jump.
Google for jump table for more info. I.e. read
this.
Of course pushing the return address and then jumping to the function address is very inefficient compared to a 'call' instruction.
while this works well on a RISC architecture on a CISC is just a time consuming operation.