DUMPBIN /EXPORTS: why does it differ between debug and release binaries?

667 views Asked by At

I'm trying to understand how to interpret the output of DUMPBIN /EXPORTS, specifically the Name column:

Binary Ordinal Hint RVA Name
x86 Debug 1 0 00090B67 ??0ImBitVector@@QAE@$$QAU0@@Z = @ILT+7010(??0ImBitVector@@QAE@$$QAU0@@Z)
x64 Debug 1 0 000958ED ??0ImBitVector@@QEAA@$$QEAU0@@Z = @ILT+2280(??0ImBitVector@@QEAA@$$QEAU0@@Z)
x86 Release 1 0 00001C80 ??0ImBitVector@@QAE@$$QAU0@@Z = ??0ImBitVector@@QAE@$$QAU0@@Z (public: __thiscall ImBitVector::ImBitVector(struct ImBitVector &&))
x64 Release 1 0 00001900 ??0ImBitVector@@QEAA@$$QEAU0@@Z = ??0ImBitVector@@QEAA@$$QEAU0@@Z (public: __cdecl ImBitVector::ImBitVector(struct ImBitVector &&))

Here's what I've understood for the moment:

Binary Meaning
Debug entry point = some index in the ILT? (entry point again?)
Release entry point = entry point again? (prototype/declaration)

Is that just redundant information in the output or not at all?

More exactly,

  • why is that name repeated after the equal sign?

  • why is that encompassed with @ILT for debug binaries?

1

There are 1 answers

1
Jerry Coffin On BEST ANSWER

This is done for the sake of incremental linking. As far as I know, Microsoft has never published exact details, but if you do a debug build with incremental linking disabled, the result looks the same as for a release build.

For example, let's start with nearly the simplest possible DLL:

__declspec(dllexport) int trash(int) { return 0; }

If we do a normal debug build, with: cl /LD /Zi trash.cpp, the output from dumpbin /exports is:

1    0 00001A91 ?trash@@YAHH@Z = @ILT+2700(?trash@@YAHH@Z)

But if we change the build to: cl /LD /Zi trash.cpp /link /incremental:no, and do a dumpbin again, we get:

1    0 00001000 ?trash@@YAHH@Z = ?trash@@YAHH@Z (int __cdecl trash(int))

...just like a release build produces.

Although it's only a guess (as I said, I don't know of Microsoft having published any details) the basic idea here is to separate parts of the file that change more frequently from parts that change less frequently, so then the linker modifies the executable in an incremental link, it won't have to rewrite as much of the file, and/or the incremental link may succeed more often so it doesn't have to write a whole new executable as often. But as I said, that part's only a guess.

As to the redundancy in the output, I believe (but no longer have the right tools installed to confirm) that this is to deal with the possibility of exporting by ordinal. If you export by ordinal the exported name will be something like @1. But dumpbin can find the real name, so if you'd exported the function by ordinal, you might get output something on this order:

1    0 00001000 @1 = ?trash@@YAHH@Z (int __cdecl trash(int))

So what's on the left of the = is the exported ordinal, then after the equal gives the name that maps to, followed by the undecorated name.

But that's going from distant memories--exporting by ordinal was mostly a 16-bit Windows thing that's been (at least unofficially) deprecated for years (and for x64, I don't think it's supported any more at all).