Create Virtual __stdcall Function That Won’t Crash When Called Using A Fn Ptr

Please support our C++ advertiser: Programming Forums - DaniWeb Sister Site
Reply

Join Date: Jul 2008
Posts: 171
Reputation: Frederick2 has a spectacular aura about Frederick2 has a spectacular aura about Frederick2 has a spectacular aura about 
Solved Threads: 20
Frederick2 Frederick2 is offline Offline
Junior Poster

Create Virtual __stdcall Function That Won’t Crash When Called Using A Fn Ptr

 
0
  #1
Sep 7th, 2008
I have not been able to obtain an answer to this question anywhere (including here) so I’ll ask it again. I can understand that folks don’t want to wade through hundreds of lines of code looking for someone else’s bug, so I’ve worked very hard to produce the very smallest possible program I can to illustrate my difficulty. Below is a working program using all __cdecl function calling – and I know I could have eliminated the __cdecl as that is the default. However, I put it in to emphasize the fact that in the next program it will be replaced with __stdcall. Here is the program and the output is directly following…

  1. #include <stdio.h>
  2.  
  3. struct IX
  4. {
  5. virtual void __cdecl Fx1()=0;
  6. virtual void __cdecl Fx2()=0;
  7. };
  8.  
  9.  
  10. struct IY
  11. {
  12. virtual void __cdecl Fy1()=0;
  13. virtual void __cdecl Fy2()=0;
  14. };
  15.  
  16.  
  17. class CA : public IX, public IY
  18. {
  19. virtual void __cdecl Fx1(){printf("Called Fx1()\n");}
  20. virtual void __cdecl Fx2(){printf("Called Fx2()\n");}
  21. virtual void __cdecl Fy1(){printf("Called Fy1()\n");}
  22. virtual void __cdecl Fy2(){printf("Called Fy2()\n");}
  23. };
  24.  
  25.  
  26. int main(void)
  27. {
  28. void (__cdecl*pFn)(void); //function pointer to void function that returns void
  29. unsigned int* ptrVTbl=0; //pointer to Vtable. There are two,e.g., an IX & IY
  30. unsigned int* VTable=0; //Virtual Function Table for IX and IY, respectively
  31. unsigned int i=0,j=0; //iterator variables
  32. CA* pCA=0; //ptr to class CA
  33.  
  34. printf("sizeof(CA) = %u\n\n", sizeof(CA));
  35. pCA=new CA;
  36. ptrVTbl=(unsigned int*)pCA;
  37. printf("i\t&ptrVTbl[]\t&VTable[]\tVTable[]\tpFn()\n");
  38. printf("====================================================================\n");
  39. for(i=0;i<2;i++)
  40. {
  41. VTable=(unsigned int*)ptrVTbl[i];
  42. for(j=0;j<2;j++)
  43. {
  44. printf
  45. (
  46. "%u\t%u\t\t%u\t\t%u\t\t", //format specifiers
  47. i, //iterator
  48. &ptrVTbl[i], //bytes 0-3 offset from pCA for i=0 and 4-7 when i=1
  49. &VTable[j], //address of function pointer held in VTable
  50. VTable[j] //value of function pointer in Vtable, i.e., address of function
  51. );
  52. pFn=(void(__cdecl*)())VTable[j]; //call function through function pointer
  53. pFn();
  54. }
  55. }
  56. printf("\n");
  57. getchar();
  58.  
  59. return 0;
  60. }
  61.  
  62. //sizeof(CA) = 8
  63. //
  64. //i &ptrVTbl[] &VTable[] VTable[] pFn()
  65. //====================================================================
  66. //0 3146560 4214960 4198544 Called Fx1()
  67. //0 3146560 4214964 4198560 Called Fx2()
  68. //1 3146564 4214952 4198576 Called Fy1()
  69. //1 3146564 4214956 4198592 Called Fy2()
  70. //Press any key to continue
As can be seen, the above program works perfectly, produces perfect output, and generates no GPFs. In case you havn’t figured out what the program does, it obtains the addresses of the Vtable pointers from the pointer to class CA, i.e., pCA, then obtains from those (there are two – one for each Vtable) the addresses of the functions in the Vtables and calls those functions through a suitably cast function pointer. Now below is the exact same program with the __cdecl modifier replaced with __stdcall.

  1. #include <stdio.h>
  2.  
  3. struct IX
  4. {
  5. virtual void __stdcall Fx1()=0;
  6. virtual void __stdcall Fx2()=0;
  7. };
  8.  
  9.  
  10. struct IY
  11. {
  12. virtual void __stdcall Fy1()=0;
  13. virtual void __stdcall Fy2()=0;
  14. };
  15.  
  16.  
  17. class CA : public IX, public IY
  18. {
  19. virtual void __stdcall Fx1(){printf("Called Fx1()\n");}
  20. virtual void __stdcall Fx2(){printf("Called Fx2()\n");}
  21. virtual void __stdcall Fy1(){printf("Called Fy1()\n");}
  22. virtual void __stdcall Fy2(){printf("Called Fy2()\n");}
  23. };
  24.  
  25.  
  26. int main(void)
  27. {
  28. void (__stdcall*pFn)(void); //function pointer to void function that returns void
  29. unsigned int* ptrVTbl=0; //pointer to Vtable. There are two,e.g., an IX & IY
  30. unsigned int* VTable=0; //Virtual Function Table for IX and IY, respectively
  31. unsigned int i=0,j=0; //iterator variables
  32. CA* pCA=0; //ptr to class CA
  33.  
  34. printf("sizeof(CA) = %u\n\n", sizeof(CA));
  35. pCA=new CA;
  36. ptrVTbl=(unsigned int*)pCA;
  37. printf("i\t&ptrVTbl[]\t&VTable[]\tVTable[]\tpFn()\n");
  38. printf("====================================================================\n");
  39. for(i=0;i<2;i++)
  40. {
  41. VTable=(unsigned int*)ptrVTbl[i];
  42. for(j=0;j<2;j++)
  43. {
  44. printf
  45. (
  46. "%u\t%u\t\t%u\t\t%u\t\t", //format specifiers
  47. i, //iterator
  48. &ptrVTbl[i], //bytes 0-3 offset from pCA for i=0 and 4-7 when i=1
  49. &VTable[j], //address of function pointer held in VTable
  50. VTable[j] //value of function pointer in Vtable, i.e., address of function
  51. );
  52. pFn=(void(__stdcall*)())VTable[j]; //call function through function pointer
  53. pFn();
  54. }
  55. }
  56. printf("\n");
  57. getchar();
  58.  
  59. return 0;
  60. }
  61.  
  62. //sizeof(CA) = 8
  63. //
  64. //i &ptrVTbl[] &VTable[] VTable[] pFn()
  65. //====================================================================
  66. //0 3146560 4214960 4198544 Called Fx1()
  67. //0 3146560 4214964 4198560 Called Fx2()
  68. //1 3146564 4214952 4198576 Called Fy1()
  69. //1 3146564 4214956 4198592 Called Fy2()
  70. //
  71. //Press any key to continue

This program produces just as perfect output as the first using __cdecl, but generates a GPF upon exiting. I’m using Microsoft Visual C++ 6 SP5 for these programs. These particular two programs I have also tested with Dev-C++ 4.9.9.2 gcc and Code::Blocks gcc. They seem to be working fine with these latter two environments for these two specific programs, but I am convinced nontheless that something is wrong. The somewhat more complex programs from which these evolved do produce some rather odd behavior with the new Code::Blocks gcc implementation. With Dev-C++ I have not been able to produce the aberrant behavior. With the Dev-C++ setup I probably have a several years older gcc compiler implementation. I have been in the pits for days fighting this stuff, and am convinced something is amiss. I think its some kind of memory corruption, but more than that I don’t know. I do not know if the problem is a sin of ommission or comission.

Since I’m basically a Win32 Api coder I use __stdcall functions all the time, but I can’t say I’ve ever written any. When specifying a __stdcall function and writing the imlementation of the function is it I who has to set up the stack frame, deal with the registers, etc? I thought the compiler took care of all that nasty low level stuff! I don’t have to do it when I write __cdecl functions!

Can someone please help me here? At least try the programs and let me know the results?
Reply With Quote Quick reply to this message  
Join Date: Jul 2008
Posts: 171
Reputation: Frederick2 has a spectacular aura about Frederick2 has a spectacular aura about Frederick2 has a spectacular aura about 
Solved Threads: 20
Frederick2 Frederick2 is offline Offline
Junior Poster

Re: Create Virtual __stdcall Function That Won’t Crash When Called Using A Fn Ptr

 
0
  #2
Sep 7th, 2008
Well, nobody seems to be replying to my thread in terms of telling me what my error is, and I took that as being ignored. However, in the meantime I just tried a neat piece of code posted several weeks ago by vijayan121 at...

http://www.daniweb.com/forums/thread141366.html

and that crashes too after providing correct output - just like mine. I'm now beginning to wander if there isn't something wrong with the VC 6 compiler that is causing this - not my (our) code? I can't expect for someone to reveal a bug to me if there isn't any I guess.
Last edited by Frederick2; Sep 7th, 2008 at 11:59 pm. Reason: add link
Reply With Quote Quick reply to this message  
Join Date: Jun 2007
Posts: 275
Reputation: dougy83 is on a distinguished road 
Solved Threads: 45
dougy83 dougy83 is offline Offline
Posting Whiz in Training

Re: Create Virtual __stdcall Function That Won’t Crash When Called Using A Fn Ptr

 
1
  #3
Sep 8th, 2008
I tried your program, seemed to run in dev-c++ & visual-c++2008 (with the reported error). Tried debugging it, saw that the stack pointer certainly is different before call to after call (that's bad) - it's due to the function doing a ret 4, when it should be a ret 0... any idea what it's popping?? (i reckon it's 'this')

Have a look at:
http://www.codeproject.com/KB/cpp/CallConventions.aspx
http://www.hackcraft.net/cpp/MSCallingConventions/

I had a look at the default commandline for visual c++ calling convention: it's /Gm for 'thiscall' which makes me think that calling a member function (which I would assume to generally be a thiscall - as it is a non-static member function, and needs the 'this' parameter) with a stdcall is not supported. I think you got lucky that cdecl doesn't screw with the stack. I guess that's doing crazy casting to do backdoor entry into functions is not recommended.

I think dev-c++ was doing the same thing with the stack (it kept freezing in debug mode), it's just that it doesn't check that the stack's ok after every call (visual-c++ calls __RTC_CheckEsp after all your calls - that's how it knows when to report it).
Reply With Quote Quick reply to this message  
Join Date: Jul 2008
Posts: 171
Reputation: Frederick2 has a spectacular aura about Frederick2 has a spectacular aura about Frederick2 has a spectacular aura about 
Solved Threads: 20
Frederick2 Frederick2 is offline Offline
Junior Poster

Re: Create Virtual __stdcall Function That Won’t Crash When Called Using A Fn Ptr

 
0
  #4
Sep 8th, 2008
I really appreciate your looking at this Dougy83. It most definitely looks like there is a problem - as I thought. With my level of expertise I just couldn't take it any further than I did. I've printed out those links and will study them. It looks to me like I need to learn how to use a stepping debugger and understand asm code (so far I've managed without it).

My whole reason for doing this was simply to learn a few of the details of how COM objects are laid out in memory. Somehow just looking at the pictures and drawings in books didn't quite satisfy me. I needed to get my hands dirty I guess. Then, just when I thought I had it all figurred out, I kept getting these strange problems that to me anyway had all the signs of some kind of memory corruption, i.e., GPFs in MS VC++, worked perfect in Dev-CPP, and partially corrupted memory address output in Code::Blocks gcc but no GPFs. I gather from your conclusions that there are no easy 'one liner' type fixes.

I'm also wondering at this point how this affects access to COM objects (which use this memory layout) from other languages. The main language I use for PC programming is PowerBASIC (www.powerbasic.com). I mostly use VC++ for WinCE devices. From what I've read from your links and comments it sounds like that will depend on how that other language handles pushing/poping registers and other esoteric details of stack management.
Reply With Quote Quick reply to this message  
Join Date: Jul 2008
Posts: 171
Reputation: Frederick2 has a spectacular aura about Frederick2 has a spectacular aura about Frederick2 has a spectacular aura about 
Solved Threads: 20
Frederick2 Frederick2 is offline Offline
Junior Poster

Re: Create Virtual __stdcall Function That Won’t Crash When Called Using A Fn Ptr

 
1
  #5
Sep 8th, 2008
Well! The solution is obvious! If it wants a this pointer, then pass it one (to balance the stack)! This runs perfect in all three environments and no crashes!

  1. #include <stdio.h>
  2.  
  3. struct IX
  4. {
  5. virtual void __stdcall Fx1()=0;
  6. virtual void __stdcall Fx2()=0;
  7. };
  8.  
  9.  
  10. struct IY
  11. {
  12. virtual void __stdcall Fy1()=0;
  13. virtual void __stdcall Fy2()=0;
  14. };
  15.  
  16.  
  17. class CA : public IX, public IY
  18. {
  19. virtual void __stdcall Fx1(){printf("Called Fx1()\n");}
  20. virtual void __stdcall Fx2(){printf("Called Fx2()\n");}
  21. virtual void __stdcall Fy1(){printf("Called Fy1()\n");}
  22. virtual void __stdcall Fy2(){printf("Called Fy2()\n");}
  23. };
  24.  
  25. int main(void)
  26. {
  27. void (__stdcall*pFn)(int);
  28. unsigned int* ptrVTbl=0;
  29. unsigned int* VTable=0;
  30. unsigned int i=0,j=0;
  31. CA* pCA=0;
  32.  
  33. printf("sizeof(CA) = %u\n\n", sizeof(CA));
  34. pCA=new CA;
  35. ptrVTbl=(unsigned int*)pCA;
  36. printf("i\t&ptrVTbl[]\t&VTable[]\tVTable[]\tpFn()\n");
  37. printf("====================================================================\n");
  38. for(i=0;i<2;i++)
  39. {
  40. VTable=(unsigned int*)ptrVTbl[i];
  41. for(j=0;j<2;j++)
  42. {
  43. printf
  44. (
  45. "%u\t%u\t\t%u\t\t%u\t\t", //format specifiers
  46. i, //iterator
  47. &ptrVTbl[i], //bytes 0-3 offset from pCA for i=0 and 4-7 when i=1
  48. &VTable[j], //address of function pointer held in VTable
  49. VTable[j] //value of function pointer in Vtable, i.e., address of function
  50. );
  51. pFn=(void(__stdcall*)(int))VTable[j]; //call function through function pointer
  52. pFn((int)pCA); //give it something to pop, even though its not used!
  53. }
  54. }
  55. printf("\n");
  56. getchar();
  57.  
  58. return 0;
  59. }
Reply With Quote Quick reply to this message  
Reply

This thread is more than three months old.
Perhaps start a new thread instead?
Message:



Other Threads in the C++ Forum


Views: 976 | Replies: 4
Thread Tools Search this Thread



Tag cloud for C++
About Us | Contact Us | Advertise | DaniWeb | Acceptable Use Policy | RSS Feed

©2003 - 2009 DaniWeb® LLC