VTable placement...
-
A while ago, some friends and I tried an experiement where we gave a "struct", in C++, virtual functions to find out where the vtbl would be placed. Much to our surprise, it seems that the vtbl was placed at the beginning of the structure (WRT its layout in memory), as opposed to the end. Why was this a rather interesting find? Because there is code out there that makes the assumption that the address of a structure will be the same as the address of its first data member. This is not always the case. For example, in "C", if you wanted to read a structure from a disk file, you could do something similar to: struct SMyStructure sMyStruct; ::fread( &sMyStruct, sizeof( SMyStructure ), 1, pFile ); Which normally works fine, but if someone desides to try extend SMyStructure by adding virtual functions, the code may no longer work when loading previous versions of SMyStructure, and will try to load 4 extra bytes that were not really there. And what if you loaded the vtbl from the last time the object was saved?!?! I thought it would have made more sense to place the vtbl at the end of the layout in memory (other compilers do this?)... Thoughts? -=- James.
-
A while ago, some friends and I tried an experiement where we gave a "struct", in C++, virtual functions to find out where the vtbl would be placed. Much to our surprise, it seems that the vtbl was placed at the beginning of the structure (WRT its layout in memory), as opposed to the end. Why was this a rather interesting find? Because there is code out there that makes the assumption that the address of a structure will be the same as the address of its first data member. This is not always the case. For example, in "C", if you wanted to read a structure from a disk file, you could do something similar to: struct SMyStructure sMyStruct; ::fread( &sMyStruct, sizeof( SMyStructure ), 1, pFile ); Which normally works fine, but if someone desides to try extend SMyStructure by adding virtual functions, the code may no longer work when loading previous versions of SMyStructure, and will try to load 4 extra bytes that were not really there. And what if you loaded the vtbl from the last time the object was saved?!?! I thought it would have made more sense to place the vtbl at the end of the layout in memory (other compilers do this?)... Thoughts? -=- James.
As far as I know the actual vtbl position is compiler specific (sames as name mangling). So don't assume anything. CU Max
-
As far as I know the actual vtbl position is compiler specific (sames as name mangling). So don't assume anything. CU Max
> As far as I know the actual vtbl position is compiler specific (sames as name > mangling). So don't assume anything. No joke...! :P I believe you missed the point: Why would a compiler writer place the vtbl in such a way that it could break existing code out there. Placing the vtbl at the END of the structure's layout in memory would allow the data to be loaded correctly. Peace! -=- James.
-
> As far as I know the actual vtbl position is compiler specific (sames as name > mangling). So don't assume anything. No joke...! :P I believe you missed the point: Why would a compiler writer place the vtbl in such a way that it could break existing code out there. Placing the vtbl at the END of the structure's layout in memory would allow the data to be loaded correctly. Peace! -=- James.
-
A while ago, some friends and I tried an experiement where we gave a "struct", in C++, virtual functions to find out where the vtbl would be placed. Much to our surprise, it seems that the vtbl was placed at the beginning of the structure (WRT its layout in memory), as opposed to the end. Why was this a rather interesting find? Because there is code out there that makes the assumption that the address of a structure will be the same as the address of its first data member. This is not always the case. For example, in "C", if you wanted to read a structure from a disk file, you could do something similar to: struct SMyStructure sMyStruct; ::fread( &sMyStruct, sizeof( SMyStructure ), 1, pFile ); Which normally works fine, but if someone desides to try extend SMyStructure by adding virtual functions, the code may no longer work when loading previous versions of SMyStructure, and will try to load 4 extra bytes that were not really there. And what if you loaded the vtbl from the last time the object was saved?!?! I thought it would have made more sense to place the vtbl at the end of the layout in memory (other compilers do this?)... Thoughts? -=- James.
If you'd like to see one nasty effect of this vtable placement decision, check out the thread from Neville Franks called "A Serious VC++ Compiler Bug". Russell Robinson (russellr@rootsoftware.com) Author of TTMaker (Advanced Timetabling Software) http://www.rootsoftware.com
-
A while ago, some friends and I tried an experiement where we gave a "struct", in C++, virtual functions to find out where the vtbl would be placed. Much to our surprise, it seems that the vtbl was placed at the beginning of the structure (WRT its layout in memory), as opposed to the end. Why was this a rather interesting find? Because there is code out there that makes the assumption that the address of a structure will be the same as the address of its first data member. This is not always the case. For example, in "C", if you wanted to read a structure from a disk file, you could do something similar to: struct SMyStructure sMyStruct; ::fread( &sMyStruct, sizeof( SMyStructure ), 1, pFile ); Which normally works fine, but if someone desides to try extend SMyStructure by adding virtual functions, the code may no longer work when loading previous versions of SMyStructure, and will try to load 4 extra bytes that were not really there. And what if you loaded the vtbl from the last time the object was saved?!?! I thought it would have made more sense to place the vtbl at the end of the layout in memory (other compilers do this?)... Thoughts? -=- James.
I think the vtable commonly goes at the start of the structure. You can't really put the vtable at the end. Take class A and class B derived from A with more member variables. A pointer to a B object has to be a valid pointer to an A object, but if the vtable is at the end, it can't be. The A pointer would have no way of knowing where the vtable of a derived class would be. Putting the vtable at the beginning, it stays in the same place.
-
A while ago, some friends and I tried an experiement where we gave a "struct", in C++, virtual functions to find out where the vtbl would be placed. Much to our surprise, it seems that the vtbl was placed at the beginning of the structure (WRT its layout in memory), as opposed to the end. Why was this a rather interesting find? Because there is code out there that makes the assumption that the address of a structure will be the same as the address of its first data member. This is not always the case. For example, in "C", if you wanted to read a structure from a disk file, you could do something similar to: struct SMyStructure sMyStruct; ::fread( &sMyStruct, sizeof( SMyStructure ), 1, pFile ); Which normally works fine, but if someone desides to try extend SMyStructure by adding virtual functions, the code may no longer work when loading previous versions of SMyStructure, and will try to load 4 extra bytes that were not really there. And what if you loaded the vtbl from the last time the object was saved?!?! I thought it would have made more sense to place the vtbl at the end of the layout in memory (other compilers do this?)... Thoughts? -=- James.
I think you'll find that your solution doesn't actually work completely. structs and classes are the exact same thing except that structs have members public by default. So, what happens if you do this? struct a { virtual ~a(); int xa; } struct b : public a { int xb; } fread(&b, sizeof(b), 1, pfile); You would then have vtables in the middle of your data b and the data inherited from a. What this teaches you is that once you leave C, you can no longer do anything C related. Adding member functions, especially virtuals creates an entirely new environment.
-
I think the vtable commonly goes at the start of the structure. You can't really put the vtable at the end. Take class A and class B derived from A with more member variables. A pointer to a B object has to be a valid pointer to an A object, but if the vtable is at the end, it can't be. The A pointer would have no way of knowing where the vtable of a derived class would be. Putting the vtable at the beginning, it stays in the same place.
Actually, no. A valid B pointer does not have to be a valid A pointer. A valid B pointer has to be *CONVERTABLE* to a valid A pointer. There is a difference, as you can see by simply looking at the addresses returned by: B b; A* pA = &b; B* pB = &b; and examining the contents of pA and pB, they will be different in VC++ if there are vtables.
-
If you'd like to see one nasty effect of this vtable placement decision, check out the thread from Neville Franks called "A Serious VC++ Compiler Bug". Russell Robinson (russellr@rootsoftware.com) Author of TTMaker (Advanced Timetabling Software) http://www.rootsoftware.com
-
I think you'll find that your solution doesn't actually work completely. structs and classes are the exact same thing except that structs have members public by default. So, what happens if you do this? struct a { virtual ~a(); int xa; } struct b : public a { int xb; } fread(&b, sizeof(b), 1, pfile); You would then have vtables in the middle of your data b and the data inherited from a. What this teaches you is that once you leave C, you can no longer do anything C related. Adding member functions, especially virtuals creates an entirely new environment.
> You would then have vtables in the middle of your data b and the data inherited from a. Good point. I did not see it that way. > What this teaches you is that once you leave C, you can no longer do anything C related. > Adding member functions, especially virtuals creates an entirely new environment. Really? :) I have not done stuff like this, but it is nice to see it when it happens and have people wonder why. :P Peace! -=- James.
-
A while ago, some friends and I tried an experiement where we gave a "struct", in C++, virtual functions to find out where the vtbl would be placed. Much to our surprise, it seems that the vtbl was placed at the beginning of the structure (WRT its layout in memory), as opposed to the end. Why was this a rather interesting find? Because there is code out there that makes the assumption that the address of a structure will be the same as the address of its first data member. This is not always the case. For example, in "C", if you wanted to read a structure from a disk file, you could do something similar to: struct SMyStructure sMyStruct; ::fread( &sMyStruct, sizeof( SMyStructure ), 1, pFile ); Which normally works fine, but if someone desides to try extend SMyStructure by adding virtual functions, the code may no longer work when loading previous versions of SMyStructure, and will try to load 4 extra bytes that were not really there. And what if you loaded the vtbl from the last time the object was saved?!?! I thought it would have made more sense to place the vtbl at the end of the layout in memory (other compilers do this?)... Thoughts? -=- James.
Weirdly enough I needed to know where the vTable was a week or so ago. I found a Microsoft document (sorry can't remember) which stated it was at the beginning. I then did some tests to check it. It is at the beginning with the Microsoft compiler. Given that they probably won't want to break V6.0 generated code when used with V7.0 generated code, I'd guess that the next version of MSDEV will use the same layout.
Observation
Although Microsoft have documented that COM's function tables map onto C++ virtual functions, then calling conventions for registers is not the same.
For C++, Microsoft state that 'this' is passed in ECX and return values are returned in EAX.
For COM, there is no gaurantee that ECX is 'this' (although it often is). For COM, 'this' appears to be passed on the stack. I'm not sure about the concept of 'this' for COM, but I think you know what I mean :-)
Cheers
Stephen Kellett