{"id":5703,"date":"2007-05-17T11:36:00","date_gmt":"2007-05-17T11:36:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vcblog\/2007\/05\/17\/diagnosing-hidden-odr-violations-in-visual-c-and-fixing-lnk2022\/"},"modified":"2019-02-18T18:54:29","modified_gmt":"2019-02-18T18:54:29","slug":"diagnosing-hidden-odr-violations-in-visual-c-and-fixing-lnk2022","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/diagnosing-hidden-odr-violations-in-visual-c-and-fixing-lnk2022\/","title":{"rendered":"Diagnosing Hidden ODR Violations in Visual C++ (and fixing LNK2022)"},"content":{"rendered":"<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Hi, my name is Andy Rich, and I&rsquo;m a tester on the front-end compiler.<span>&nbsp; <\/span>Last time I <\/font><a href=\"http:\/\/blogs.msdn.com\/vcblog\/archive\/2006\/10\/24\/conformance-testing-and-breaking-changes.aspx\"><font face=\"Times New Roman\" size=\"3\">posted here<\/font><\/a><font face=\"Calibri\" size=\"3\">, I discussed several aspects of testing conformance in the C++ compiler, which is a large part of my job.<span>&nbsp; <\/span>Another large part of what I do is diagnosing bugs in the compiler.<span>&nbsp; <\/span>These come from a variety of sources, such as customer reports through <\/font><a href=\"http:\/\/connect.microsoft.com\/\"><font face=\"Times New Roman\" size=\"3\">Connect<\/font><\/a><font size=\"3\"><font face=\"Calibri\">, regression testing, and new tests. But some of the most useful and critical bugs come from one of our largest customers: other developers in Microsoft.<span>&nbsp; <\/span>These developers are the most likely to be using our latest product, and are often doing some very complicated things with it (such as building Windows).<span>&nbsp; <\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">What I want to discuss today is a class of bug report that we get all the time, especially from internal customers.<span>&nbsp; <\/span>We see these reports because the user is convinced they&rsquo;ve caught a compiler bug, and have contacted us for a fix.<span>&nbsp; <\/span>But I&rsquo;m not going to discuss a compiler bug &ndash; because this class of bug report is always user error.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><b>It is important that I warn you up front<\/b>: some of the tricks that I am going to use &ndash; one compiler switch in particular &ndash; are UNDOCUMENTED and UNSUPPORTED.<span>&nbsp; <\/span>If you choose to make use of this option in the compiler, you do so at your own risk.<span>&nbsp; <\/span>We use this option all the time internally, but we have basically no testing for it, so there&rsquo;s no telling what might happen if you use it.<span>&nbsp; <\/span>Still here?<span>&nbsp; <\/span>Great &ndash; let&rsquo;s get on with it.<\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\"><font size=\"3\"><b>Spot the defect<\/b> is a fun game we play in the compiler team.<span>&nbsp; <\/span>Someone sends out a snippet of code, and says &ldquo;Spot the defect!&rdquo; and then we all take potshots at the code, trying to determine where the bug is.<span>&nbsp; <\/span>(I used to play the same game with my <\/font><\/font><a href=\"http:\/\/www.ddj.com\/\"><font face=\"Times New Roman\" size=\"3\">DDJ<\/font><\/a><font face=\"Calibri\" size=\"3\"> subscription &ndash; the first thing I sought out every month was the PC-Lint <\/font><a href=\"http:\/\/www.gimpel.com\/html\/bugs.htm\"><font face=\"Times New Roman\" size=\"3\">Bug Of The Month<\/font><\/a><font face=\"Calibri\" size=\"3\"> advertisements.) Let&rsquo;s play the same game &ndash; here&rsquo;s your code, in three source files:<\/font><\/p>\n<p><span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">\/\/ a.h &ndash; share class definition (code has been corrected from original post)<\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">#pragma<\/span><span lang=\"EN\"> <span>once<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\"><span><\/p>\n<p><\/span><\/span>&nbsp;<\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\"><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">class<\/span><span lang=\"EN\"> Test_A {<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">public<\/span><span lang=\"EN\">:<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Test_A(){ c = <span>&#8216;X&#8217;<\/span>; data = 0; }<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><span>~Test_A(){ <span>if<\/span>(data) <span>delete<\/span> [] data; }<\/span><span lang=\"EN\"><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>char<\/span> c;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span>char<\/span>* data;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">};<\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\"><\/p>\n<p><\/span>&nbsp;<\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\"><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">void<\/span><span lang=\"EN\"> Test_A_Loader(Test_A&amp;);<\/span><span lang=\"EN\"><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">\/\/ loader.cpp &#8211; loader defn.<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">#include<\/span><span lang=\"EN\"> <span>&lt;cstring&gt;<\/span> <span>\/\/for strcpy<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">#include<\/span><span lang=\"EN\"> <span>&#8220;a.h&#8221;<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\"><span><\/p>\n<p><\/span><\/span>&nbsp;<\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\"><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">void<\/span><span lang=\"EN\"> Test_A_Loader(Test_A&amp; a) {<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a.c = <span>&#8216;p&#8217;<\/span>;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a.data = <span>new<\/span> <span>char<\/span>[10];<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcpy(a.data, <span>&#8220;3.14159&#8221;<\/span>);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">}<\/span><span lang=\"EN\"><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">\/\/ main.cpp &#8211; main program<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">#include<\/span><span lang=\"EN\"> <span>&lt;stdio.h&gt;<\/span> <span>\/\/ for printf<\/p>\n<p><\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">#include<\/span><span lang=\"EN\"> <span>&#8220;a.h&#8221;<\/span><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\"><span><\/p>\n<p><\/span><\/span>&nbsp;<\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\"><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">int<\/span><span lang=\"EN\"> main(){<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Test_A a;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Test_A_Loader(a);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf(<span>&#8220;%c : %c %c %c %cn&#8221;<\/span>, a.c, <\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a.data[0], a.data[1], a.data[2], a.data[3]);<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span lang=\"EN\">}<\/span><span lang=\"EN\">&nbsp;&nbsp;<\/span><\/p>\n<p><\/span><font size=\"3\"><font face=\"Calibri\">&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><b>If I told you this code was crashing<\/b>, would you believe me?<span>&nbsp; <\/span>This is simplified, but accurately represents the same class of bugs I was whining about earlier.<span>&nbsp; <\/span>The astute reader will look to the title of the post for assistance, which is helpful, though it won&rsquo;t be a big enough hint: knowing the dangers of ODR violations are key to understanding this problem. (I did say they&rsquo;d be hidden ODR violations, didn&rsquo;t I?)<\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><b>The One-Definition Rule<\/b> is defined in section 3.2 of the C++ Standard (ISO\/IEC 14882:2003).<span>&nbsp; <\/span>Paraphrasing that rule: no single translation unit can have two definitions for the same variable, function, class type, etc.<span>&nbsp; <\/span>However, between two translation units, some things can have the same definition (as in the code sample above), <i>provided they are the same in both translation units<\/i>.<span>&nbsp; <\/span>Based on this reading, it doesn&rsquo;t appear I&rsquo;m breaking this rule in my code &ndash; in fact, based on what I&rsquo;ve shown you, I haven&rsquo;t.<span>&nbsp; <\/span>Because the spot-the-defect was a trick question of sorts: there appears to be no crash in the code I showed you.<\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><b>This lie-through-omission<\/b> is the same one that internal customers tell all the time: the class appears to be the same in both translation units, and yet one translation unit is treating it differently than the other.<span>&nbsp; <\/span>They&rsquo;re left to assume that there&rsquo;s a bug in the compiler that&rsquo;s causing this.<span>&nbsp; <\/span>After a while in the compiler group, you learn to ask the right questions; and the right question, in this case, is: how is this program being built?<span>&nbsp; <\/span>Because in this case, that&rsquo;s where the defect lies:<\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span>cl main.cpp \/Zp2 \/c<br \/>cl loader.cpp \/Zp1 \/c<br \/>link main.obj loader.obj<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><b><font face=\"Calibri\" size=\"3\">As soon as I see the <\/font><\/b><a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/xh3e3fd0(VS.80).aspx\"><font face=\"Times New Roman\"><font size=\"3\"><b>\/Zp<\/b> <b>switch<\/b><\/font><\/font><\/a><font face=\"Calibri\" size=\"3\"> (or I notice code that has a lot of #pragma pack&rsquo;s), I start getting suspicious that I&rsquo;m about to see an ODR violation.<span>&nbsp; <\/span>The issue is actually fairly simple, once you understand it &ndash; the problem is that it is rarely as obvious as this case &ndash; classes have lots of base classes, are embedded in other classes, and often only show issues when you move to another architecture (especially when moving from 32 to 64-bit, because pointer sizes change).<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><b>The defect<\/b> is that the definition of Test_A is different between main.obj and loader.obj.<span>&nbsp; <\/span>So main creates the object with one view, and loader goes and stomps the data passed to it with its own understanding of Test_A, then that object is returned by-value to main.obj, which then tries to access an invalid pointer.<\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Main.obj&rsquo;s understanding of Test_A:<\/font><\/p>\n<p class=\"MsoNormal\"><span>class Test_A<span>&nbsp;&nbsp;&nbsp; <\/span>size(6):<br \/><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>+&#8212;<br \/><span>&nbsp;<\/span>0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>| c<br \/><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>| &lt;alignment member&gt; (size=1)<br \/><span>&nbsp;<\/span>2<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>| data<br \/><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>+&#8212; <\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Loader.obj&rsquo;s understanding of Test_A:<\/font><\/p>\n<p class=\"MsoNormal\"><span>class Test_A<span>&nbsp;&nbsp;&nbsp; <\/span>size(5):<br \/><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>+&#8212;<br \/><span>&nbsp;<\/span>0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>| c<br \/><span>&nbsp;<\/span>1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>| data<br \/><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>+&#8212;<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><b>You can clearly see<\/b> that the <\/font><\/font><span>data<\/span><font face=\"Calibri\" size=\"3\"> pointer is not in the same place in both of these objects.<span>&nbsp; <\/span>This looks like a bug of the compiler, but the compiler is explicitly complying with the user&rsquo;s request, and because the two compilands are compiled as separate entities, there&rsquo;s no way the compiler could diagnose this.<span>&nbsp; <\/span>The linker can diagnose this in limited scenarios, especially under IJW.<span>&nbsp; <\/span>If you&rsquo;ve gotten the boggling &ldquo;inconsistent metadata&rdquo; error (<\/font><a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/zkf0dz41(VS.80).aspx\"><font face=\"Times New Roman\" size=\"3\">LNK2022<\/font><\/a><font face=\"Calibri\" size=\"3\">), you&rsquo;ve probably fallen victim to this issue.<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><b>Diagnosing and fixing these issues<\/b>, once you&rsquo;ve run into them, is the real challenge.<span>&nbsp; <\/span>The LNK2022 help page is especially informative, stating: &ldquo;In this case, make sure that the type has an identical definition in all compilands.&rdquo;<span>&nbsp; <\/span>(Which is harder to do &ndash; especially when packing throws a wrench in there.) The compiler has a switch that can help you do this, but (as I said before) that switch is undocumented and unsupported.<span>&nbsp; <\/span>It&rsquo;s also fantastically helpful.<span>&nbsp; <\/span>That switch is \/d1reportSingleClassLayoutXXX, where XXX performs substring matches against the class name.<span>&nbsp; <\/span>In fact, those helpful class layout tables above were created with that switch:<\/font><\/font><\/p>\n<p class=\"MsoNormal\"><span>cl main.cpp \/Zp2 \/c \/d1reportSingleClassLayoutTest_A<\/span><\/p>\n<p class=\"MsoNormal\"><font size=\"3\"><font face=\"Calibri\"><b>Using this switch<\/b> in multiple compilands and then comparing the output is an unbelievably useful way to get to the bottom of these issues.<span>&nbsp; <\/span>It shows you exactly what the compiler believes to be the layout of that class &ndash; if these layouts differ at all, you&rsquo;ve got potential bugs waiting to be found.<span>&nbsp; <\/span>The switch is more than just useful &ndash; it can be fun to inspect and see how things are laid out by the compiler.<\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\"><font size=\"3\"><b>One other helpful tip<\/b> for getting to the bottom of these problems is to make use of <\/font><\/font><a href=\"http:\/\/msdn2.microsoft.com\/en-us\/library\/2e70t5y1(VS.80).aspx\"><font face=\"Times New Roman\" size=\"3\">#pragma pack(show)<\/font><\/a><font face=\"Calibri\" size=\"3\">, which you can put right before your object definition to see what the packing level is when the compiler hits that line.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Thanks for reading,<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Calibri\" size=\"3\">Andy<\/font><\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hi, my name is Andy Rich, and I&rsquo;m a tester on the front-end compiler.&nbsp; Last time I posted here, I discussed several aspects of testing conformance in the C++ compiler, which is a large part of my job.&nbsp; Another large part of what I do is diagnosing bugs in the compiler.&nbsp; These come from a [&hellip;]<\/p>\n","protected":false},"author":289,"featured_media":35994,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-5703","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus"],"acf":[],"blog_post_summary":"<p>Hi, my name is Andy Rich, and I&rsquo;m a tester on the front-end compiler.&nbsp; Last time I posted here, I discussed several aspects of testing conformance in the C++ compiler, which is a large part of my job.&nbsp; Another large part of what I do is diagnosing bugs in the compiler.&nbsp; These come from a [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/5703","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/users\/289"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=5703"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/5703\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/35994"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=5703"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=5703"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=5703"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}