What is DLL import hinting?

Binding and hinting are two types of optimizations to improve the load-time performance of a module (executable or DLL). We'll start with hinting, then look at binding, and then look at how it affects delay-loading.

The import table for a module contains a list of DLLs and a list of functions from that DLL which the module wishes to link to. The basic idea is that for each target DLL, the linker loads the DLL and then obtains the address of each imported function and from that DLL, records the results in the loaded module's table of imported function addresses.

Hinting is a technique for speeding up this lookup.

In addition to recording the name of the function the module wishes to link to, the linker also records the index of the function in the target DLL's export table. For example, suppose we had a DLL named FLINT whose export table looks like this:

1 Barney
2 Fred
3 Wilma

I've numbered the entries for reasons you'll see soon.

You wrote a DLL which imports all three of these functions. The import table for your DLL goes something like this:

Import from FLINT
Fred (hint = 2)
Wilma (hint = 3)
Barney (hint = 1)

When your DLL gets loaded, the module loader loads the target DLL FLINT.DLL, and then it goes about resolving the imports. First up is Fred. But instead of just searching the export table of FLINT.DLL for the function Fred, it sees the hint and says, "Hey, let me look in slot 2 first." And lo and behold, there the function is in slot 2. Yay, a full search of the export table was not necessary; the hint sent us directly to the correct slot.

The hint is just a hint, though. If FLINT.DLL doesn't have the function Fred in slot 2, then the loader just does things the old-fashioned way, by searching the export table for the Fred function.

In general, hints are fairly resilient as long as the DLL doesn't change too much. If FLINT.DLL is updated, say by a security patch, the list of functions typically does not change, so the position in the exported names table remains unchanged. It's only if a function is added to or removed from FLINT.DLL do the hints begin to lose their effectiveness.

Next time, we'll look at binding, which is an optimization even more powerful than hinting, but which is also more fragile.

Comments (10)
  1. Tom says:

    Man, does this topic have legs!

    My questions below might violate some implied ground rules, so feel free to delete or ignore this comment.

    Raymond: do you have any thoughts on how prevalent these load-time optimizations might be?  For example, are these techniques common when building the Windows Shell?

    I know that this is not really a .NET blog, but a thought crosses my mind — would these techniques work on assemblies?  My gut feeling is that they wouldn’t since the functions exported by assemblies must first be JIT-ed, but maybe these techniques could be applied to assemblies that have been pre-translated (ngen).

  2. Jared says:

    I may be missing something, but this seems like a trivia boost.  The number of entries in a dll should be relatively small, and in an ordered table.

    When a dll has hundreds of entry points, perhaps it’s getting too big?

    Maybe the real performance problem is an application having to loading a zillion bytes of dlls when only a few percent of the loaded coded is actually referenced (or potentially usable)?

    [The export table is sorted by ordinal number, which isn’t much help when you’re looking by name. (And may I suggest reading up on “demand paging.”) -Raymond]
  3. Doogal says:

    I also wonder what kind of performance boost this would provide. Is it just a throwback to ye olde days where this would make a difference or is it still worth it? And if it’s the latter, what are the steps required by searching the export table that make it slow?

  4. Adrian says:

    @Tom:  I just did a dumpbin /exports of several common system modules like user32.dll.  There you can see that those DLLs export functions by name and by ordinal.

    Until a few minutes ago, I thought hints and ordinals were the same things. (I know I’ve read other articles that implied that and even used the terms interchangeably.)  But dumpbin shows different numbers for ordinal and hint.  So now I’m confused again.

    My best guess is that the ordinal is just a 32-bit number that, if assigned to an exported function, can be used in place of the name of the function–comparing numbers is generally faster than comparing strings.  The hint, then, would be the index into the export name table, which is possibly specific to the version of the DLL.

    To use ordinals, I know you have to do extra work–specifically, create a .DEF file that specifies the ordinals and optionally tells the linker to discard the name.  Does hinting happen automatically?

  5. Cheong says:

    I’ve been used to bind function to the index back in the Win 3.1 days.

    The reason is because TPW 1.5 online help say that this kind of bind is faster (and it is). Also my primary use if the API involved (WinExec()) is for calling up winmine.exe in the CS lesson after assignment is done. I don’t have to consider maintainance afterwards. :P

  6. Myria says:

    Are exports’ names always in lexicographical order, so that when hint lookup fails, it can do a binary search by name?

    Regarding the questions above, the hints come from the import library you statically linked against.

    Since the ordinal numbers differ among Windows versions, this hinting ultimately isn’t very useful.

    Some ordinals don’t change between versions, such as the older functions of ws2_32.dll.  This is because to this day, their import libraries still use ordinal-only lookup.

    [Export names are not always in lexicographical order. If you explicitly assign ordinals, then they appear in the order you specified. I discussed this a few years ago.-Raymond]
  7. Drak says:


    I’d imagine that if you imported one of your own DLLs the ordinals wouldn’t change between windows versions.

    (Disclaimer: I only do .Net programming for Windows so what I just said might be utter rubbish)

  8. In my executable protector loader code I’m also using an additional hint which are the first letters of the API (skipping anything else A..Z like _ @ etc.) so the export table search loop is even faster, import table resolving is the biggest issue in my software (in terms of speed, scanning an export table of 20MB dll file with >50 chars in the export names takes sooo long), but there are some things that can be done to speed it up, eg. when you have a DLL loaded you can split a single IMAGE_IMPORT_DESCRIPTOR resolving across multiple threads (for the PE EXE files of course). The best solution to speed up import table resolving would be using only ordinals, blazing fast ;)

  9. Random832 says:

    If it fails to find it at the slot specified in the hint, does it search nearby positions first? (i.e. the hint says 42, it’s not there, does it search 40 41 43 44 etc, or does it start from 1?)

  10. Kyle says:


    Maybe the real performance problem is an application having to loading a zillion bytes of dlls when only a few percent of the loaded coded is actually referenced (or potentially usable)?


    Actually, executable modules aren’t loaded into memory wholesale, but rather are mapped into memory through the use of sections.  In essence, Windows kind of treats them as a sort of read-only page file, loading pages of the executable at a time.  This reduces load times and also frees up physical memory for other use.

Comments are closed.