Fix Win32 calling convention mismatch in DLL exports - #5
Open
MaxLogic wants to merge 1 commit into
Open
Conversation
Declare the managed DLL exports as cdecl and add real Win32 Release DUnitX coverage for validation and parsing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This changes the Delphi declarations for the unmanaged
parseandisValidNumberexports fromstdcalltocdecl, matching the callingconvention used by
PhoneNumbersUnmanaged.dll.It also adds DUnitX interop regression tests that exercise the real Win32 DLL
in Release configuration.
Root cause
PhoneNumbersUnmanaged/PhoneNumbersWrapper.csexports both functions using[DllExport]without specifying another calling convention. These exports usecdecl, whilePhoneNumbers.pasdeclared the corresponding Delphi functionpointers as
stdcall.On Win32, the two conventions disagree about stack cleanup:
cdecl, the caller removes the arguments from the stack.stdcall, the caller expects the callee to remove them.The DLL therefore leaves the arguments on the stack, while the Delphi caller
compiled from the
stdcalldeclaration assumes they have already beenremoved. This leaves the Win32 stack pointer unbalanced and can cause an access
violation after returning from the DLL.
Why the problem can remain hidden
The mismatch is configuration-dependent and does not necessarily fail during
manual testing.
The supplied sample project uses Win32 Debug settings with optimization
disabled and stack-frame generation enabled. A generated stack-frame epilogue
can restore
ESPfromEBPwhen the Delphi method returns, accidentallyrepairing the imbalance before it becomes visible.
Win64 is also unaffected in practice because Windows x64 uses a unified calling
convention, so Delphi's
cdeclandstdcalldeclarations do not produce thesame stack-cleanup disagreement as on Win32.
The original declarations produced the following observed results:
ParseIsValidNumberFor Win32 Release,
IsValidNumbercrashed consistently with exception code0xC0000005for both valid and invalid input.Parsehappened to survive dueto its different generated call-site and managed return-value handling, but
its declaration was still ABI-incompatible and therefore unsafe.
This explains how both the supplied sample and applications using the wrapper
could previously have appeared to work.
Changes
parsefunction pointer ascdecl.isValidNumberfunction pointer ascdecl.Debug stack frames can mask this class of ABI defect.