{"id":31355,"date":"2022-12-13T17:01:41","date_gmt":"2022-12-13T17:01:41","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cppblog\/?p=31355"},"modified":"2022-12-13T17:01:41","modified_gmt":"2022-12-13T17:01:41","slug":"standards-conformance-improvements-to-gw-in-visual-studio-version-17-5-preview-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/standards-conformance-improvements-to-gw-in-visual-studio-version-17-5-preview-2\/","title":{"rendered":"Standards conformance improvements to \/Gw in Visual Studio version 17.5 Preview 2"},"content":{"rendered":"<p>The <a href=\"https:\/\/learn.microsoft.com\/en-us\/cpp\/build\/reference\/gw-optimize-global-data\">\/Gw switch<\/a> enables the linker to optimize global data to reduce binary size. As part of the 17.5 Preview 2 release a new flag, <code>\/Zc:checkGwOdr[-]<\/code><em>,<\/em> has been added to improve C++ standards conformance when using <code>\/Gw<\/code>. Previously, when using <code>\/Gw<\/code>, certain One Definition Rule (ODR) violations were being ignored and would not cause an error. The new flag ensures that we do raise the appropriate errors. If you are currently using <code>\/Gw<\/code> we recommend setting <code>\/Zc:checkGwOdr<\/code> on your builds, as it\u2019s currently off by default. This may change in a future major update. For a more detailed explanation of ODR, <code>\/Gw<\/code>, and how this issue came about, read on below.<\/p>\n<p>Let\u2019s go through three definitions that we need for background here:<\/p>\n<ol>\n<li>First up are COMDATs, a short description is that COMDATs are extra segments that data can be placed in to enable the linker to potentially fold out said data from the binary. Importantly, these sections are flagged with a strategy for how duplicates are handled. For a deeper dive into the history of COMDATs, see <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20161024-00\/?p=94575\">Raymond\u2019s blog post which covers their use and history<\/a>.<\/li>\n<li>Next, just what does <code>\/Gw<\/code> do? For a full explanation, check out the <a href=\"https:\/\/devblogs.microsoft.com\/cppblog\/introducing-gw-compiler-switch\/\">blog post announcing the switch<\/a>, but for our purposes the flag enables the compiler to place global data into COMDATs. This lets us optimize out unreferenced globals, or merge identical globals via their COMDAT sections<\/li>\n<li>Finally, the One Definition Rule (ODR) <a href=\"https:\/\/en.wikipedia.org\/wiki\/One_Definition_Rule\">as described on Wikipedia<\/a>.<\/li>\n<\/ol>\n<p>With the background out of the way we\u2019ll go through a simple but common enough example:<\/p>\n<p><strong>odr.h:<\/strong><\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">#pragma once\r\n\/\/ defining a global here is an ODR violation\r\n\/\/ the initialization to 0 isn\u2019t required to cause a violation\r\n\/\/ but is needed to exhibit the incorrect \/Gw behavior\r\nint MyGlobal = 0;<\/code><\/pre>\n<p><strong>bar.cpp:<\/strong><\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">#include \"odr.h\"\r\nint bar() {\r\n    return 2;\r\n}<\/code><\/pre>\n<p><strong>foo.cpp:<\/strong><\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">#include \"odr.h\"\r\nint foo() {\r\n   return 1;\r\n}\r\n\r\nextern int bar();\r\n\r\nint main() {\r\n   foo();\r\n   bar();\r\n}\r\n<\/code><\/pre>\n<p>Compiling this without <code>\/Gw<\/code> leads to an error as we\u2019d expect:<\/p>\n<pre class=\"prettyprint language-default\"><code class=\"language-default\">cl \/nologo foo.cpp bar.cpp\r\nfoo.cpp\r\nbar.cpp\r\nGenerating Code...\r\nbar.obj : error LNK2005: \"int MyGlobal\" (?MyGlobal@@3HA) already defined in foo.obj\r\nfoo.exe : fatal error LNK1169: one or more multiply defined symbols found<\/code><\/pre>\n<p>Since we defined <code>MyGlobal<\/code> in <code>odr.h<\/code>, we end up with a definition in both <code>foo.obj<\/code> &amp; <code>bar.obj<\/code>, causing the linker to report the ODR violation. Now, if we compile with <code>\/Gw<\/code>:<\/p>\n<pre class=\"prettyprint language-default\"><code class=\"language-default\">cl \/nologo \/Gw foo.cpp bar.cpp\r\nfoo.cpp\r\nbar.cpp\r\nGenerating Code...<\/code><\/pre>\n<p>We don\u2019t end up with an error, so what happened? It ends up coming back to the COMDAT flags mentioned above. Looking at the obj headers, we can see that <code>MyGlobal<\/code> has indeed been placed in a COMDAT:<\/p>\n<pre class=\"prettyprint language-default\"><code class=\"language-default\">link.exe \/dump \/headers foo.obj\r\n\u2026\r\nSECTION HEADER #3\r\n    .bss name\r\n    \u2026\r\nC0301080 flags\r\n         Uninitialized Data\r\n         COMDAT; sym= \"int MyGlobal\" (?MyGlobal@@3HA)\r\n         4 byte align\r\n         Read Write<\/code><\/pre>\n<p>What this doesn\u2019t show is that this COMDAT has been flagged as PICKANY. So, when multiple definitions that would be candidates for merging are found the linker arbitrarily picks one of them and discards the rest. This is odd though, when <code>\/Gw<\/code> is enabled this COMDAT was flagged with NOMATCH on creation. NOMATCH, as the name implies, means that the linker should raise an error if a duplicate is found, exactly what we want. So, what went wrong?<\/p>\n<p>The key here is that the definition of <code>MyGlobal<\/code> includes an assignment to zero. This causes another optimization to kick in. Since this global is initialized to zero, we notice that it can be moved into the <a href=\"https:\/\/en.wikipedia.org\/wiki\/.bss\">.bss section<\/a>. Since we don\u2019t have to store the data for this global if it\u2019s in <code>.bss<\/code>, moving the COMDAT lets us reduce the object file size. Unfortunately, when we move the COMDAT the flag was being reset from NOMATCH to PICKANY, causing our bug.<\/p>\n<p>As of 17.5 Preview 2 you can now use the new flag to make sure you\u2019re not hiding these ODR violations on accident with <code>\/Gw<\/code>:<\/p>\n<pre class=\"prettyprint language-default\"><code class=\"language-default\">cl \/nologo \/Gw \/Zc:checkGwOdr foo.cpp bar.cpp\r\nfoo.cpp\r\nbar.cpp\r\nGenerating Code...\r\nbar.obj : error LNK2005: \"int MyGlobal\" (?MyGlobal@@3HA) already defined in foo.obj\r\nfoo.exe : fatal error LNK1169: one or more multiply defined symbols found<\/code><\/pre>\n<p>With the error exposed, we can make the global extern in our header, and move the definition to one of the cpp files to resolve the problem:<\/p>\n<p><strong>odr.h:\n<\/strong><\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">#pragma once\r\nextern int MyGlobal;<\/code><\/pre>\n<p><strong>foo.cpp:<\/strong><\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">#include \"odr.h\"\r\nint MyGlobal = 0;\r\n\u2026<\/code><\/pre>\n<pre class=\"prettyprint language-default\"><code class=\"language-default\">cl \/nologo \/Gw \/Zc:checkGwOdr foo.cpp bar.cpp\r\nfoo.cpp\r\nbar.cpp\r\nGenerating Code...<\/code><\/pre>\n<p>Alternatively, for C++17 and higher, you can use an <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/inline\">inline specifier<\/a> on the definition:<\/p>\n<p><strong>odr.h:<\/strong><\/p>\n<pre class=\"prettyprint language-cpp\"><code class=\"language-cpp\">inline int MyGlobal = 0;<\/code><\/pre>\n<p>Usually fixing an ODR violation looks something like this, though not every case will be so simple. We encourage using <code>\/Zc:checkGwOdr<\/code> to prevent these violations from creeping into your builds if you\u2019re using <code>\/Gw<\/code>. As this is a standards conformance issue, we may change the default behavior of <code>\/Gw<\/code> to imply <code>\/Zc:checkGwOdr<\/code> in a future release.<\/p>\n<h4>Send us your feedback<\/h4>\n<p>We hope you found these details interesting! If you have ideas for similar posts you\u2019d like to see, please let us know. We are also interested in your feedback to continue to improve our tools. The comments below are open. Feedback can also be shared through\u00a0<a href=\"https:\/\/developercommunity.visualstudio.com\/cpp\" target=\"_blank\" rel=\"noopener\">Developer Community<\/a>. You can also reach us on Twitter (<a href=\"https:\/\/twitter.com\/visualc\" target=\"_blank\" rel=\"noopener\">@VisualC<\/a>), or via email at\u00a0<a href=\"mailto:visualcpp@microsoft.com\">visualcpp@microsoft.com<\/a>.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The \/Gw switch enables the linker to optimize global data to reduce binary size. As part of the 17.5 Preview 2 release a new flag, \/Zc:checkGwOdr[-], has been added to improve C++ standards conformance when using \/Gw. Previously, when using \/Gw, certain One Definition Rule (ODR) violations were being ignored and would not cause an [&hellip;]<\/p>\n","protected":false},"author":107351,"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-31355","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cplusplus"],"acf":[],"blog_post_summary":"<p>The \/Gw switch enables the linker to optimize global data to reduce binary size. As part of the 17.5 Preview 2 release a new flag, \/Zc:checkGwOdr[-], has been added to improve C++ standards conformance when using \/Gw. Previously, when using \/Gw, certain One Definition Rule (ODR) violations were being ignored and would not cause an [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/31355","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\/107351"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=31355"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/31355\/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=31355"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=31355"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=31355"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}