Reputation: 22265
Say, if a DLL has a function:
int TestFunc01(int v)
{
WCHAR buff[256];
::StringCchPrintf(buff, _countof(buff), L"You passed a value of %d", v);
return ::MessageBox(NULL, buff, L"Test Dll Message", MB_OK | MB_ICONINFORMATION);
}
that is exported by its ordinal value only (the following is a .def
file):
LIBRARY DllName
EXPORTS
TestFunc01 @1 NONAME
So now when I want to statically link to that function from another module, I'd do the following if the function was exported by its name:
extern "C" __declspec(dllimport) int TestFunc01(int v);
int _tmain(int argc, _TCHAR* argv[])
{
TestFunc01(123);
}
But how do I statically link to it only by its ordinal value?
PS. I'm using a Visual Studio C++ compiler & linker.
Upvotes: 3
Views: 1607
Reputation: 22265
This is not to compete with the actual answer. David Heffernan raised a good point in the comments there, about the use of dllimport
. So I thought to make several tests to see what difference does it make when I use __declspec(dllimport)
and when I don't:
Function declaration in the DLL:
extern "C" int __cdecl TestFunc01(int v)
{
WCHAR buff[256];
::StringCchPrintf(buff, _countof(buff), L"You passed a value of %d", v);
return ::MessageBox(NULL, buff, L"Test Dll Message", MB_OK | MB_ICONINFORMATION);
}
then import and call it from another module:
__declspec(dllimport)
extern "C" __declspec(dllimport) int __cdecl TestFunc01(int v);
int _tmain(int argc, _TCHAR* argv[])
{
TestFunc01(123);
}
compiled machine code:
The call
instruction reads the function address from the Import Address Table (IAT):
that gives it the location of imported function:
__declspec(dllimport)
extern "C" int __cdecl TestFunc01(int v);
int _tmain(int argc, _TCHAR* argv[])
{
TestFunc01(123);
}
compiled machine code:
In this case it is a relative call
to a single jmp
instruction:
which in turn reads the function address from IAT:
and jumps to it:
For 64-bit we have to change the calling convention to __fastcall
. The rest stays the same:
__declspec(dllimport)
extern "C" __declspec(dllimport) int __fastcall TestFunc01(int v);
int _tmain(int argc, _TCHAR* argv[])
{
TestFunc01(123);
}
compiled machine code:
Again the call
instruction reads the function address from IAT (in case of x64 it uses relative address):
that gives it the function address:
__declspec(dllimport)
extern "C" int __fastcall TestFunc01(int v);
int _tmain(int argc, _TCHAR* argv[])
{
TestFunc01(123);
}
compiled machine code:
Again a relative call
to a single jmp
:
that in turn reads the function address from IAT:
and jumps to it:
So as you see, by not using dllimport
you're technically incurring an additional jmp
. I'm not exactly sure what is the purpose of that jump, but it is sure not to make your code run faster. Maybe it's a code maintenance thing, maybe it's a way to do hot-patching for updates, maybe a debugging feature. So if someone can shed some light on the purpose of that jump I'll be glad to hear it.
Upvotes: 0
Reputation: 33706
But how do I statically link to it only by its ordinal value?
absolute the same way, when function exported by name - no any difference. in both case you need 2 things - correct declaration of the function:
extern "C" __declspec(dllimport) int /*calling convention*/ TestFunc01(int v);
and lib file, included to linker input.
when you include somename.def file to visual studio project, it automatically add /def:"somename.def"
option to linker (otherwise you need manual add this option). generated lib file will be containing __imp_*TestFunc01*
symbol - in place * will be different decoration based on c or c++ symbol and calling convention, in case x86.
from another side, when you call function, with __declspec(dllimport)
attribute. compiler (CL) will be generate call [__imp_*TestFunc01*]
- so reference to __imp_*TestFunc01*
symbol (again * in place actual decoration). linker will be search for __imp_*TestFunc01*
symbol and found it in lib file.
the NONAME
option does not matter for this process - this this only affects how it will be formed IAT/INT entry for this function in PE (will be it imported by name or ordinal)
note that if we separate generate lib file from def file only, by link.exe /lib /def:somename.def
- the linker will be have not correct declarations for exported functions (def file containing name only without calling convention and c or c++ name) - so it always will be considered symbols as extern "C"
and __cdecl
in concrete case visible that in dll function implemented as int TestFunc01(int v)
- so without extern "C"
- as result in lib file will be symbol like __imp_?TestFunc01@@YAHH@Z
(i assume __cdecl and x86), but in another module function used with extern "C"
- so linker will be search for __imp__TestFunc01
and of course not found it, because it not exist in lib file. because this, when we export/import some symbol - it must be equal declared for both modules. the best declare it in separate .h file with explicit calling convention
Upvotes: 3
Reputation: 15162
You can use the lib.exe tool to create a .lib file from a .DEF file that you write, you do not have to actually provide any object files.
You would write a .def that matches the ordinals you want to export but also provide names and then use lib.exe to create a .lib.
In code you would then declare it as: extern "C" __declspec(dllimport) ret_type funcName(arg_type);
It is important that the calling convention match whatever the function actually uses but the name would have to match what your created .lib uses even if that name doesn't follow the decoration normal for that call type.
Upvotes: 3