// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // // This file provides an explicit check of field layouts using some macro magic // in VerifyLayouts.h. The goal is that if any field changes offset or type changes // size then a build time assert should trigger. DO NOT change these definitions without // reading the comments in VerifyLayouts.h and understanding what other code you need // to change at the same time. // // // AN EXAMPLE: // You want to validate the fields in type CDerived // // class CFoo // { // void* m_ptrField; // } // // struct BigStruct // { // DWORD m_one; // DWORD m_two; // DWORD m_three; // } // // CDerived : CFoo // { // DWORD m_cRef; // SomeOtherType* m_otherType; // #ifdef _SOME_DEFINE // BigStruct m_struct; // #endif //_SOME_DEFINE // } // // // and the layout validation is written as: // // BEGIN_TYPE(CDerived, sizeof(CFoo)) // a) The first field starts at sizeof(CFoo) to account for base class data // // b) Beware of vtable pointers, they take up space before the data fields too // // c) Beware of using sizeof(some_other_type) unless you also explicitly verify // // the layout of that type here. Changing the base type would change the derived types // // layout and won't be caught unless the base type is explicitly verified. // // d) sizeof() is fine - they never change over time and we know how // // to deal with platform pointer size differences // FIELD(CDerived, m_cRef, 4) // FIELD(CDerived, m_otherType, sizeof(void*)) // #ifdef _SOME_DEFINE_ // ALIGN_FIELD(CDerived, m_struct, sizeof(BigStruct), 4) // We need to use the ALIGN_FIELD macro because the alignment isn't the same as // // the field size. The alignment of a structure is typically the max alignment // // of any member. The alignment of a primitive type is typically the size of the type. // #endif _SOME_DEFINE_ // END_TYPE(CDerived, sizeof(void*) // // // BEGIN_TYPE(CFoo, 0) // We must verify this type because we used it to define the starting offset of CDerived // FIELD(CFoo, m_ptrField, sizeof(void*)) // END_TYPE(CFoo, sizeof(void*)) // // // BEGIN_TYPE(BigStruct, 0) // We must verify this type because we used sizeof(BigStruct) to define the size of // // CDerived::m_struct field // FIELD(BigStruct, m_one, 4) // FIELD(BigStruct, m_two, 4) // FIELD(BigStruct, m_thee, 4) // END_TYPE(BigStruct, 4) // // // // // OTHER CONSIDERATIONS: // // 1) if the type layout is conditional on a define, just include the same define here in the layout verification // Make sure that the define is registered in the list of defines and the debugger reading code knows how to dynamically // adjust for it (See VerifyLayouts.h comment item (b) and (c)) // // 2) If your type names use characters that aren't valid identifiers (such as the '<' and '>' chars in templates) // then you need to provide an escaped name. There are variations of the macros above to do that: // BEGIN_TYPE_ESCAPED(typeName, escaptedTypeName, firstFieldOffset) // FIELD_ESCAPED(typeName, escapedTypeName, fieldName, fieldSize) // ALIGN_FIELD_ESCAPED(typeName, escapedTypeName, fieldName, fieldSize, fieldAlign) // END_TYPE_ESCAPED(typeName, escapedTypeName, typeAlignment) // // If CFoo above had instead been CFoo we would write: // BEGIN_TYPE_ESCAPED(CFoo, CFoo__ULONG__, 0) // FIELD_ESCAPED(CFoo, CFoo__ULONG__, m_ptrField, sizeof(void*)) // END_TYPE_ESCAPED(CFoo, CFoo__ULONG__, sizeof(void*)) // // The escapedTypeName is relatively arbitrary, but convention is to replace the illegal characters with double underscore // The name does show up in build error messages, so it should close to the real name for people to understand // // 3) If your type name has commas in it (such as in a list of template arguments) the macros could interpret it as seperate // arguments (no good). To fix this use the IGNORE_COMMAS() macro to escape any typeNames with commas. If CFoo above had // been CFoo you could rewrite the defines as: // BEGIN_TYPE_ESCAPED(IGNORE_COMMAS(CFoo), CFoo__ULONG__INT__, 0) // FIELD_ESCAPED(IGNORE_COMMAS(CFoo), CFoo__ULONG__INT__, m_ptrField, sizeof(void*)) // END_TYPE_ESCAPED(IGNORE_COMMAS(CFoo), CFoo__ULONG__INT__, sizeof(void*)) // // 4) If you have a bitfield in your type, the offsetof macro can't be used which will break the static asserts. // There is a special BITFIELD macro that can work around it: // BITFIELD(typeName, fieldName, expectedFieldOffset, fieldSize, fieldAlign) // // The macro is just like FIELD execpt you must provide the offset yourself. Since you can't use offsetof on the field directly // the convention is to use the offset of the previous field and then add the size and alignment requirements. For example if your // type had this: // CMiniMdRW // { // ULONG m_cbSaveSize; // int m_fIsReadOnly : 1; // int m_bPreSaveDone : 1; // int m_bSaveCompressed : 1; // int m_bPostGSSMod : 1; // } // // You could write // FIELD(CMiniMdRW, m_cbSaveSize, 4) // BITFIELD(CMiniMdRW, m_fIsReadOnly, offsetof(CMiniMdRW, m_cbSaveSize)+4, 4) // // Don't include al the fields in the bitfield, just pick one as the canonical field name // // // // HOW DO I DEBUG THIS STUFF WHEN THE BUILD DOESN'T WORK? // // One way that has been effective for me is to write a few static_assert_no_msg entries manually in VerifyLayouts.h // You can use those to verify your assumptions such as: // static_assert_no_msg(sizeof(Foo) == 24) // static_assert_no_msg(offset(Foo, m_lastField) == 20) // static_assert_no_msg(offset_of_field_affter_Foo_m_lastField == 24) // Then rebuild and find out where the compiler disagress with you. // // Another option is to run the source through the preprocessor // // // BEGIN_TYPE(MDInternalRW, 2*sizeof(void*)) FIELD(MDInternalRW, m_pStgdb, sizeof(void*)) FIELD(MDInternalRW, m_tdModule, 4) FIELD(MDInternalRW, m_cRefs, 4) FIELD(MDInternalRW, m_fOwnStgdb, 4) FIELD(MDInternalRW, m_pUnk, sizeof(void*)) FIELD(MDInternalRW, m_pUserUnk, sizeof(void*)) FIELD(MDInternalRW, m_pIMetaDataHelper, sizeof(void*)) FIELD(MDInternalRW, m_pSemReadWrite, sizeof(void*)) FIELD(MDInternalRW, m_fOwnSem, 4) END_TYPE(MDInternalRW, sizeof(void*)) BEGIN_TYPE(CLiteWeightStgdbRW, sizeof(CLiteWeightStgdb)) FIELD(CLiteWeightStgdbRW, m_cbSaveSize, 4) FIELD(CLiteWeightStgdbRW, m_bSaveCompressed, 4) FIELD(CLiteWeightStgdbRW, m_pImage, sizeof(void*)) FIELD(CLiteWeightStgdbRW, m_dwImageSize, 4) FIELD(CLiteWeightStgdbRW, m_dwPEKind, 4) FIELD(CLiteWeightStgdbRW, m_dwMachine, 4) FIELD(CLiteWeightStgdbRW, m_pStreamList, sizeof(void*)) FIELD(CLiteWeightStgdbRW, m_pNextStgdb, sizeof(void*)) FIELD(CLiteWeightStgdbRW, m_eFileType, 4) FIELD(CLiteWeightStgdbRW, m_wszFileName, sizeof(void*)) FIELD(CLiteWeightStgdbRW, m_dwDatabaseLFT, 4) FIELD(CLiteWeightStgdbRW, m_dwDatabaseLFS, 4) FIELD(CLiteWeightStgdbRW, m_pStgIO, sizeof(void*)) END_TYPE(CLiteWeightStgdbRW, 8) BEGIN_TYPE_ESCAPED(CLiteWeightStgdb, CLiteWeightStgdb__CMiniMdRW__, 0) ALIGN_FIELD_ESCAPED(CLiteWeightStgdb, CLiteWeightStgdb__CMiniMdRW__, m_MiniMd, sizeof(CMiniMdRW), sizeof(void*)) FIELD_ESCAPED(CLiteWeightStgdb, CLiteWeightStgdb__CMiniMdRW__, m_pvMd, sizeof(void*)) FIELD_ESCAPED(CLiteWeightStgdb, CLiteWeightStgdb__CMiniMdRW__, m_cbMd, 4) END_TYPE_ESCAPED(CLiteWeightStgdb, CLiteWeightStgdb__CMiniMdRW__, sizeof(void*)) BEGIN_TYPE(CMiniMdRW, sizeof(CMiniMdTemplate)) FIELD(CMiniMdRW, m_pMemberRefHash, sizeof(void*)) FIELD(CMiniMdRW, m_pMemberDefHash, sizeof(void*)) ALIGN_FIELD(CMiniMdRW, m_pLookUpHashs, sizeof(void*)*TBL_COUNT, sizeof(void*)) ALIGN_FIELD(CMiniMdRW, m_StringPoolOffsetHash, sizeof(MapSHash), 4) FIELD(CMiniMdRW, m_pNamedItemHash, sizeof(void*)) FIELD(CMiniMdRW, m_maxRid, 4) FIELD(CMiniMdRW, m_limRid, 4) FIELD(CMiniMdRW, m_maxIx, 4) FIELD(CMiniMdRW, m_limIx, 4) FIELD(CMiniMdRW, m_eGrow, 4) ALIGN_FIELD(CMiniMdRW, m_Tables, sizeof(RecordPool)*TBL_COUNT, sizeof(void*)) ALIGN_FIELD(CMiniMdRW, m_pVS, sizeof(void*)*TBL_COUNT, sizeof(void*)) ALIGN_FIELD(CMiniMdRW, m_StringHeap, sizeof(StgStringPool), sizeof(void*)) ALIGN_FIELD(CMiniMdRW, m_BlobHeap, sizeof(StgBlobPool), sizeof(void*)) ALIGN_FIELD(CMiniMdRW, m_UserStringHeap, sizeof(StgBlobPool), sizeof(void*)) ALIGN_FIELD(CMiniMdRW, m_GuidHeap, sizeof(StgGuidPool), sizeof(void*)) FIELD(CMiniMdRW, m_pHandler, sizeof(void*)) FIELD(CMiniMdRW, m_cbSaveSize, 4) BITFIELD(CMiniMdRW, m_fIsReadOnly, offsetof(CMiniMdRW, m_cbSaveSize)+4, 4) FIELD(CMiniMdRW, m_pMethodMap, sizeof(void*)) FIELD(CMiniMdRW, m_pFieldMap, sizeof(void*)) FIELD(CMiniMdRW, m_pPropertyMap, sizeof(void*)) FIELD(CMiniMdRW, m_pEventMap, sizeof(void*)) FIELD(CMiniMdRW, m_pParamMap, sizeof(void*)) FIELD(CMiniMdRW, m_pFilterTable, sizeof(void*)) FIELD(CMiniMdRW, m_pHostFilter, sizeof(void*)) FIELD(CMiniMdRW, m_pTokenRemapManager, sizeof(void*)) ALIGN_FIELD(CMiniMdRW, m_OptionValue, sizeof(OptionValue), 4) ALIGN_FIELD(CMiniMdRW, m_StartupSchema, sizeof(CMiniMdSchema), 8) ALIGN_FIELD(CMiniMdRW, m_bSortable, sizeof(BYTE)*TBL_COUNT, sizeof(BYTE)) #ifdef _DEBUG FIELD(CMiniMdRW, dbg_m_pLock, sizeof(void*)) #endif FIELD(CMiniMdRW, m_fMinimalDelta, 4) FIELD(CMiniMdRW, m_rENCRecs, sizeof(void*)) END_TYPE(CMiniMdRW, 8) BEGIN_TYPE(OptionValue, 0) FIELD(OptionValue, m_DupCheck, 4) FIELD(OptionValue, m_RefToDefCheck, 4) FIELD(OptionValue, m_NotifyRemap, 4) FIELD(OptionValue, m_UpdateMode, 4) FIELD(OptionValue, m_ErrorIfEmitOutOfOrder, 4) FIELD(OptionValue, m_ThreadSafetyOptions, 4) FIELD(OptionValue, m_ImportOption, 4) FIELD(OptionValue, m_LinkerOption, 4) FIELD(OptionValue, m_GenerateTCEAdapters, 4) FIELD(OptionValue, m_RuntimeVersion, sizeof(void*)) FIELD(OptionValue, m_MetadataVersion, 4) FIELD(OptionValue, m_MergeOptions, 4) FIELD(OptionValue, m_InitialSize, 4) FIELD(OptionValue, m_LocalRefPreservation, 4) END_TYPE(OptionValue, sizeof(void*)) BEGIN_TYPE(StgBlobPool, sizeof(StgPool)) ALIGN_FIELD(StgBlobPool, m_Hash, sizeof(CBlobPoolHash), sizeof(void*)) END_TYPE(StgBlobPool, sizeof(void*)) BEGIN_TYPE(StgStringPool, sizeof(StgPool)) ALIGN_FIELD(StgStringPool, m_Hash, sizeof(CStringPoolHash), sizeof(void*)) FIELD(StgStringPool, m_bHash, sizeof(BOOL)) END_TYPE(StgStringPool, sizeof(void*)) BEGIN_TYPE(StgGuidPool, sizeof(StgPool)) ALIGN_FIELD(StgGuidPool, m_Hash, sizeof(CGuidPoolHash), sizeof(void*)) FIELD(StgGuidPool, m_bHash, sizeof(BOOL)) END_TYPE(StgGuidPool, sizeof(void*)) BEGIN_TYPE(RecordPool, sizeof(StgPool)) FIELD(RecordPool, m_cbRec, 4) END_TYPE(RecordPool, sizeof(void*)) BEGIN_TYPE(StgPool, sizeof(StgPoolReadOnly)) FIELD(StgPool, m_ulGrowInc, 4) FIELD(StgPool, m_pCurSeg, sizeof(void*)) FIELD(StgPool, m_cbCurSegOffset, 4) BITFIELD(StgPool, m_bFree, offsetof(StgPool, m_cbCurSegOffset)+4, 4) // can't take offsetof on a bitfield so we have to provide the offset another way FIELD(StgPool, m_nVariableAlignmentMask, 4) FIELD(StgPool, m_cbStartOffsetOfEdit, 4) FIELD(StgPool, m_fValidOffsetOfEdit, 4) END_TYPE(StgPool, sizeof(void*)) BEGIN_TYPE(CStringPoolHash, sizeof(CChainedHash)) FIELD(CStringPoolHash, m_Pool, sizeof(void*)) END_TYPE(CStringPoolHash, sizeof(void*)) BEGIN_TYPE(CBlobPoolHash, sizeof(CChainedHash)) FIELD(CBlobPoolHash, m_Pool, sizeof(void*)) END_TYPE(CStringPoolHash, sizeof(void*)) BEGIN_TYPE(CGuidPoolHash, sizeof(CChainedHash)) FIELD(CGuidPoolHash, m_Pool, sizeof(void*)) END_TYPE(CGuidPoolHash, sizeof(void*)) BEGIN_TYPE_ESCAPED(MetaData::HotHeap, MetaData__HotHeap, 0) FIELD_ESCAPED(MetaData::HotHeap, MetaData__HotHeap, m_pHotHeapHeader, sizeof(void*)) END_TYPE_ESCAPED(MetaData::HotHeap, MetaData__HotHeap, sizeof(void*)) BEGIN_TYPE(StgPoolReadOnly, sizeof(StgPoolSeg) + sizeof(void*)) //vtable pointer ALIGN_FIELD(StgPoolReadOnly, m_HotHeap, sizeof(MetaData::HotHeap), sizeof(void*)) END_TYPE(StgPoolReadOnly, sizeof(void*)) BEGIN_TYPE_ESCAPED(IGNORE_COMMAS(MapSHash), MapSHash__ULONG__ULONG, sizeof(void*)) FIELD_ESCAPED(IGNORE_COMMAS(MapSHash), MapSHash__ULONG__ULONG, m_table, sizeof(void*)) FIELD_ESCAPED(IGNORE_COMMAS(MapSHash), MapSHash__ULONG__ULONG, m_tableSize, 4) FIELD_ESCAPED(IGNORE_COMMAS(MapSHash), MapSHash__ULONG__ULONG, m_tableCount, 4) FIELD_ESCAPED(IGNORE_COMMAS(MapSHash), MapSHash__ULONG__ULONG, m_tableOccupied, 4) FIELD_ESCAPED(IGNORE_COMMAS(MapSHash), MapSHash__ULONG__ULONG, m_tableMax, 4) END_TYPE_ESCAPED(IGNORE_COMMAS(MapSHash), MapSHash__ULONG__ULONG, sizeof(void*)) BEGIN_TYPE(StgPoolSeg, 0) FIELD(StgPoolSeg, m_pSegData, sizeof(void*)) FIELD(StgPoolSeg, m_pNextSeg, sizeof(void*)) FIELD(StgPoolSeg, m_cbSegSize, 4) FIELD(StgPoolSeg, m_cbSegNext, 4) END_TYPE(StgPoolSeg, sizeof(void*)) BEGIN_TYPE_ESCAPED(CChainedHash, CCHainedHash_STRINGHASH, sizeof(void*)) // vtable pointer FIELD_ESCAPED(CChainedHash, CCHainedHash_STRINGHASH, m_rgData, sizeof(void*)) FIELD_ESCAPED(CChainedHash, CCHainedHash_STRINGHASH, m_iBuckets, 4) FIELD_ESCAPED(CChainedHash, CCHainedHash_STRINGHASH, m_iSize, 4) FIELD_ESCAPED(CChainedHash, CCHainedHash_STRINGHASH, m_iCount, 4) FIELD_ESCAPED(CChainedHash, CCHainedHash_STRINGHASH, m_iMaxChain, 4) FIELD_ESCAPED(CChainedHash, CCHainedHash_STRINGHASH, m_iFree, 4) END_TYPE_ESCAPED(CChainedHash, CCHainedHash_STRINGHASH, sizeof(void*)) BEGIN_TYPE(CMiniColDef, 0) FIELD(CMiniColDef, m_Type, 1) FIELD(CMiniColDef, m_oColumn, 1) FIELD(CMiniColDef, m_cbColumn, 1) END_TYPE(CMiniColDef, 1) BEGIN_TYPE(CMiniTableDef, 0) FIELD(CMiniTableDef, m_pColDefs, sizeof(void*)) FIELD(CMiniTableDef, m_cCols, 1) FIELD(CMiniTableDef, m_iKey, 1) FIELD(CMiniTableDef, m_cbRec, 1) END_TYPE(CMiniTableDef, sizeof(void*)) BEGIN_TYPE(CMiniMdBase, 8) //vtable ptr and first field 8-byte alignment ALIGN_FIELD(CMiniMdBase, m_Schema, sizeof(CMiniMdSchema), 8) FIELD(CMiniMdBase, m_TblCount, 4) FIELD(CMiniMdBase, m_fVerifiedByTrustedSource, 4) ALIGN_FIELD(CMiniMdBase, m_TableDefs, sizeof(CMiniTableDef)*TBL_COUNT, sizeof(void*)) FIELD(CMiniMdBase, m_iStringsMask, 4) FIELD(CMiniMdBase, m_iGuidsMask, 4) FIELD(CMiniMdBase, m_iBlobsMask, 4) END_TYPE(CMiniMdBase, 8) BEGIN_TYPE(CMiniMdSchemaBase, 0) FIELD(CMiniMdSchemaBase, m_ulReserved, 4) FIELD(CMiniMdSchemaBase, m_major, 1) FIELD(CMiniMdSchemaBase, m_minor, 1) FIELD(CMiniMdSchemaBase, m_heaps, 1) FIELD(CMiniMdSchemaBase, m_rid, 1) FIELD(CMiniMdSchemaBase, m_maskvalid, 8) FIELD(CMiniMdSchemaBase, m_sorted, 8) END_TYPE(CMiniMdSchemaBase, 8) BEGIN_TYPE(CMiniMdSchema, sizeof(CMiniMdSchemaBase)) ALIGN_FIELD(CMiniMdSchema, m_cRecs, 4*TBL_COUNT, 4) FIELD(CMiniMdSchema, m_ulExtra, 4) END_TYPE(CMiniMdSchema, 8)