{"id":183,"date":"2015-03-10T02:36:06","date_gmt":"2015-03-10T02:36:06","guid":{"rendered":"http:\/\/blogs.gentoo.org\/blueness\/?p=183"},"modified":"2015-03-10T16:39:18","modified_gmt":"2015-03-10T16:39:18","slug":"the-c11-abi-incompatibility-problem-in-gentoo","status":"publish","type":"post","link":"https:\/\/blogs.gentoo.org\/blueness\/2015\/03\/10\/the-c11-abi-incompatibility-problem-in-gentoo\/","title":{"rendered":"The C++11 ABI incompatibility problem in Gentoo"},"content":{"rendered":"<p>Gentoo allows users to have multiple versions of gcc installed and we (mostly?) support systems where userland is partially build with different versions.\u00a0 There are both advantages and disadvantages to this and in this post, I&#8217;m going to talk about one of the disadvantages, the C++11 ABI incompatibility problem.\u00a0 I don&#8217;t exactly have a solution, but at least we can define what the problem is and track it [1].<\/p>\n<p>First what is C++11?\u00a0 Its a new standard of C++ which is just now making its way through GCC and clang as experimental.\u00a0 The current default standard is C++98 which you can verify by just reading the defined value of __cplusplus using the preprocessor.<\/p>\n<pre>$\u00a0 g++ -x c++ -E -P - &lt;&lt;&lt; __cplusplus\r\n199711L\r\n$\u00a0 g++ -x c++ --std=c++98 -E -P - &lt;&lt;&lt; __cplusplus\r\n199711L\r\n$\u00a0 g++ -x c++ --std=c++11 -E -P - &lt;&lt;&lt; __cplusplus\r\n201103L\r\n<\/pre>\n<p>This shouldn&#8217;t be surprising, even good old C has standards:<\/p>\n<pre>$ gcc -x c -std=c90 -E -P - &lt;&lt;&lt; __STDC_VERSION__\r\n__STDC_VERSION__\r\n$ gcc -x c -std=c99 -E -P - &lt;&lt;&lt; __STDC_VERSION__\r\n199901L\r\n$ gcc -x c -std=c11 -E -P - &lt;&lt;&lt; __STDC_VERSION__\r\n201112L\r\n<\/pre>\n<p>We&#8217;ll leave the interpretation of these values as an exercise to the reader.\u00a0 [2]<\/p>\n<p>The specs for these different standards at least allow for different syntax and semantics in the language.\u00a0 So here&#8217;s an example of how C++98 and C++11 differ in this respect:<\/p>\n<pre>\/\/ I build with both --std=c++98 and --std=c++11\r\n#include &lt;iostream&gt;\r\nusing namespace std;\r\nint main() {\r\n    int i, a[] = { 5, -3, 2, 7, 0 };\r\n    for (i = 0; i &lt; sizeof(a)\/sizeof(int); i++)\r\n        cout &lt;&lt; a[i] &lt;&lt; endl ;\r\n    return 0;\r\n}\r\n<\/pre>\n<pre>\/\/ I build with only --std=c++11\r\n#include &lt;iostream&gt;\r\nusing namespace std;\r\nint main() {\r\n    int a[] = { 5, -3, 2, 7, 0 };\r\n    for (auto&amp; x : a)\r\n        cout &lt;&lt; x &lt;&lt; endl ;\r\n    return 0;\r\n}\r\n<\/pre>\n<p>I think most people would agree that the C++11 way of iterating over arrays (or other objects like vectors) is sexy.\u00a0 In fact C++11 is filled with sexy syntax, especially when it come to its threading and atomics, and so coders are seduced.\u00a0 This is an upstream choice and it should be reflected in their build system with &#8211;std= sprinkled where needed.\u00a0 I hope you see why you should never add &#8211;std= to your CFLAGS or CXXFLAGS.<\/p>\n<p>The syntactic\/semantic differences is the first &#8220;incompatiblity&#8221; and it is really not our problem downstream.\u00a0 Our problem in Gentoo comes because of ABI incompatibilities between the two standards arrising from two sources: 1) Linking between objects compiled with &#8211;std=c++98 and &#8211;std=c++11 is not guaranteed to work.\u00a0 2) Neither is linking between objects both compiled with &#8211;std=c+11 but with different versions of GCC differing in their minior release number.\u00a0 (The minor release number is x in gcc-4.x.y.)<\/p>\n<p>To see this problem in action, let&#8217;s consider the following little snippet of code which uses a C++11 only function [3]<\/p>\n<pre>#include &lt;chrono&gt;\r\nusing namespace std;\r\nint main() {\r\n    auto x = chrono::steady_clock::now;\r\n}\r\n<\/pre>\n<p>Now if we compile that with gcc-4.8.3 and check its symbols we get the following:<\/p>\n<pre>$ $ g++ --version\r\ng++ (Gentoo Hardened 4.8.3 p1.1, pie-0.5.9) 4.8.3\r\n$ g++ --std=c++11 -c test.cpp\r\n$ readelf -s test.o\r\nSymbol table '.symtab' contains 12 entries:\r\nNum:\u00a0\u00a0\u00a0 Value\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Size Type\u00a0\u00a0\u00a0 Bind\u00a0\u00a0 Vis\u00a0\u00a0\u00a0\u00a0\u00a0 Ndx Name\r\n  0: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 NOTYPE\u00a0 LOCAL\u00a0 DEFAULT\u00a0 UND\r\n  1: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 FILE\u00a0\u00a0\u00a0 LOCAL\u00a0 DEFAULT\u00a0 ABS test.cpp\r\n  2: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 SECTION LOCAL\u00a0 DEFAULT\u00a0\u00a0\u00a0 1\r\n  3: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 SECTION LOCAL\u00a0 DEFAULT\u00a0\u00a0\u00a0 3\r\n  4: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 SECTION LOCAL\u00a0 DEFAULT\u00a0\u00a0\u00a0 4\r\n  5: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 SECTION LOCAL\u00a0 DEFAULT\u00a0\u00a0\u00a0 6\r\n  6: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 SECTION LOCAL\u00a0 DEFAULT\u00a0\u00a0\u00a0 7\r\n  7: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 SECTION LOCAL\u00a0 DEFAULT\u00a0\u00a0\u00a0 5\r\n  8: 0000000000000000\u00a0\u00a0\u00a0 78 FUNC\u00a0\u00a0\u00a0 GLOBAL DEFAULT\u00a0\u00a0\u00a0 1 main\r\n  9: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 NOTYPE\u00a0 GLOBAL DEFAULT\u00a0 UND _GLOBAL_OFFSET_TABLE_\r\n 10: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 NOTYPE\u00a0 GLOBAL DEFAULT\u00a0 UND _ZNSt6chrono3_V212steady_\r\n 11: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 NOTYPE\u00a0 GLOBAL DEFAULT\u00a0 UND __stack_chk_fail\r\n<\/pre>\n<p>We can now confirm that that symbol is in fact in libstdc++.so for 4.8.3 but NOT for 4.7.3 as follows:<\/p>\n<pre>$ readelf -s \/usr\/lib\/gcc\/x86_64-pc-linux-gnu\/4.8.3\/libstdc++.so.6 | grep _ZNSt6chrono3_V212steady_\r\n  1904: 00000000000e5698\u00a0\u00a0\u00a0\u00a0 1 OBJECT\u00a0 GLOBAL DEFAULT\u00a0\u00a0 13 _ZNSt6chrono3_V212steady_@@GLIBCXX_3.4.19\r\n  3524: 00000000000c8b00\u00a0\u00a0\u00a0 89 FUNC\u00a0\u00a0\u00a0 GLOBAL DEFAULT\u00a0\u00a0 11 _ZNSt6chrono3_V212steady_@@GLIBCXX_3.4.19\r\n$ readelf -s \/usr\/lib\/gcc\/x86_64-pc-linux-gnu\/4.7.3\/libstdc++.so.6 | grep _ZNSt6chrono3_V212steady_\r\n$\r\n<\/pre>\n<p>Okay, so we&#8217;re just seeing an example of things in flux.\u00a0 Big deal?\u00a0 If you finish linking test.cpp and check what it links against you get what you expect:<\/p>\n<pre>$ g++ --std=c++11 -o test.gcc48 test.o\r\n$ .\/test.gcc48\r\n$ ldd test.gcc48\r\n        linux-vdso.so.1 (0x000002ce333d0000)\r\n        libstdc++.so.6 =&gt; \/usr\/lib\/gcc\/x86_64-pc-linux-gnu\/4.8.3\/libstdc++.so.6 (0x000002ce32e88000)\r\n        libm.so.6 =&gt; \/lib64\/libm.so.6 (0x000002ce32b84000)\r\n        libgcc_s.so.1 =&gt; \/usr\/lib\/gcc\/x86_64-pc-linux-gnu\/4.8.3\/libgcc_s.so.1 (0x000002ce3296d000)\r\n        libc.so.6 =&gt; \/lib64\/libc.so.6 (0x000002ce325b1000)\r\n        \/lib64\/ld-linux-x86-64.so.2 (0x000002ce331af000)\r\n<\/pre>\n<p>Here&#8217;s where the wierdness comes in.\u00a0 Suppose we now switch to gcc-4.7.3 and repeat.\u00a0 Things don&#8217;t quite work as expected:<\/p>\n<pre>$ g++ --version\r\ng++ (Gentoo Hardened 4.7.3-r1 p1.4, pie-0.5.5) 4.7.3\r\n$ g++ --std=c++11 -o test.gcc47 test.cpp\r\n$ ldd test.gcc47\r\n        linux-vdso.so.1 (0x000003bec8a9c000)\r\n        libstdc++.so.6 =&gt; \/usr\/lib\/gcc\/x86_64-pc-linux-gnu\/4.8.3\/libstdc++.so.6 (0x000003bec8554000)\r\n        libm.so.6 =&gt; \/lib64\/libm.so.6 (0x000003bec8250000)\r\n        libgcc_s.so.1 =&gt; \/usr\/lib\/gcc\/x86_64-pc-linux-gnu\/4.8.3\/libgcc_s.so.1 (0x000003bec8039000)\r\n        libc.so.6 =&gt; \/lib64\/libc.so.6 (0x000003bec7c7d000)\r\n        \/lib64\/ld-linux-x86-64.so.2 (0x000003bec887b000)\r\n<\/pre>\n<p>Note that it says its linking against 4.8.3\/libstdc++.so.6 and not 4.7.3.\u00a0 That&#8217;s because of the order in which the library paths are search is defined in \/etc\/ld.so.conf.d\/05gcc-x86_64-pc-linux-gnu.conf and this file is sorted that way it is on purpose.\u00a0 So maybe it&#8217;ll run!\u00a0 Let&#8217;s try:<\/p>\n<pre>$ .\/test.gcc47\r\n.\/test.gcc47: relocation error: .\/test.gcc47: symbol _ZNSt6chrono12steady_clock3nowEv, version GLIBCXX_3.4.17 not defined in file libstdc++.so.6 with link time reference\r\n<\/pre>\n<p>Nope, no joy.\u00a0 So what&#8217;s going on?\u00a0 Let&#8217;s look at the symbols in both test.gcc47 and test.gcc48:<\/p>\n<pre>$ readelf -s test.gcc47\u00a0 | grep chrono\r\n  9: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 FUNC\u00a0\u00a0\u00a0 GLOBAL DEFAULT\u00a0 UND _ZNSt6chrono12steady_cloc@GLIBCXX_3.4.17 (4)\r\n 50: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 FUNC\u00a0\u00a0\u00a0 GLOBAL DEFAULT\u00a0 UND _ZNSt6chrono12steady_cloc\r\n$ readelf -s test.gcc48\u00a0 | grep chrono\r\n  9: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 FUNC\u00a0\u00a0\u00a0 GLOBAL DEFAULT\u00a0 UND _ZNSt6chrono3_V212steady_@GLIBCXX_3.4.19 (4)\r\n 49: 0000000000000000\u00a0\u00a0\u00a0\u00a0 0 FUNC\u00a0\u00a0\u00a0 GLOBAL DEFAULT\u00a0 UND _ZNSt6chrono3_V212steady_\r\n<\/pre>\n<p>Whoah!\u00a0 The symbol wasn&#8217;t mangled the same way!\u00a0 Looking more carefully at *all* the chrono symbols in 4.8.3\/libstdc++.so.6 and 4.7.3\/libstdc++.so.6 we see the problem.<\/p>\n<pre>$ readelf -s \/usr\/lib\/gcc\/x86_64-pc-linux-gnu\/4.8.3\/libstdc++.so.6 | grep chrono\r\n  353: 00000000000e5699\u00a0\u00a0\u00a0\u00a0 1 OBJECT\u00a0 GLOBAL DEFAULT\u00a0\u00a0 13 _ZNSt6chrono3_V212system_@@GLIBCXX_3.4.19\r\n 1489: 000000000005e0e0\u00a0\u00a0\u00a0 86 FUNC\u00a0\u00a0\u00a0 GLOBAL DEFAULT\u00a0\u00a0 11 _ZNSt6chrono12system_cloc@@GLIBCXX_3.4.11\r\n 1605: 00000000000e1a3f\u00a0\u00a0\u00a0\u00a0 1 OBJECT\u00a0 GLOBAL DEFAULT\u00a0\u00a0 13 _ZNSt6chrono12system_cloc@@GLIBCXX_3.4.11\r\n 1904: 00000000000e5698\u00a0\u00a0\u00a0\u00a0 1 OBJECT\u00a0 GLOBAL DEFAULT\u00a0\u00a0 13 _ZNSt6chrono3_V212steady_@@GLIBCXX_3.4.19\r\n 2102: 00000000000c8aa0\u00a0\u00a0\u00a0 86 FUNC\u00a0\u00a0\u00a0 GLOBAL DEFAULT\u00a0\u00a0 11 _ZNSt6chrono3_V212system_@@GLIBCXX_3.4.19\r\n 3524: 00000000000c8b00\u00a0\u00a0\u00a0 89 FUNC\u00a0\u00a0\u00a0 GLOBAL DEFAULT\u00a0\u00a0 11 _ZNSt6chrono3_V212steady_@@GLIBCXX_3.4.19\r\n$ readelf -s \/usr\/lib\/gcc\/x86_64-pc-linux-gnu\/4.7.3\/libstdc++.so.6 | grep chrono\r\n 1478: 00000000000c6260\u00a0\u00a0\u00a0 72 FUNC\u00a0\u00a0\u00a0 GLOBAL DEFAULT\u00a0\u00a0 12 _ZNSt6chrono12system_cloc@@GLIBCXX_3.4.11\r\n 1593: 00000000000dd9df\u00a0\u00a0\u00a0\u00a0 1 OBJECT\u00a0 GLOBAL DEFAULT\u00a0\u00a0 14 _ZNSt6chrono12system_cloc@@GLIBCXX_3.4.11\r\n 2402: 00000000000c62b0\u00a0\u00a0\u00a0 75 FUNC\u00a0\u00a0\u00a0 GLOBAL DEFAULT\u00a0\u00a0 12 _ZNSt6chrono12steady_cloc@@GLIBCXX_3.4.17\r\n<\/pre>\n<p>Only 4.7.3\/libstdc++.so.6 has _ZNSt6chrono12steady_cloc@@GLIBCXX_3.4.17.\u00a0 Normally when libraries change their exported symbols, they change their SONAME, but this is not the case here, as running `readelf -d` on both shows.\u00a0 GCC doesn&#8217;t bump the SONAME that way for reasons explained in [4].\u00a0 Great, so just switch around the order of path search in \/etc\/ld.so.conf.d\/05gcc-x86_64-pc-linux-gnu.conf.\u00a0 Then we get the problem the other way around:<\/p>\n<pre>$ .\/test.gcc47\r\n$ .\/test.gcc48\r\n.\/test.gcc48: \/usr\/lib\/gcc\/x86_64-pc-linux-gnu\/4.7.3\/libstdc++.so.6: version `GLIBCXX_3.4.19' not found (required by .\/test.gcc48)\r\n<\/pre>\n<p>So no problem if your system has only gcc-4.7.\u00a0 No problem if it has only 4.8.\u00a0 But if it has both, then compiling C++11 with 4.7 and linking against libstdc++ for 4.8 (or vice versa) and you get breakage at the binary level.\u00a0 This is the C++11 ABI incompatibility problem in Gentoo.\u00a0 As an exercise for the reader, fix!<\/p>\n<p>Ref.<\/p>\n<p>[1] <a href=\"https:\/\/bugs.gentoo.org\/show_bug.cgi?id=542482\" target=\"_blank\">Bug 542482 &#8211; (c++11-abi) [TRACKER] c++11 abi incompatibility<\/a><\/p>\n<p>[2] This is an old professor&#8217;s trick for saying, hey go find out why c90 doesn&#8217;t define a value for __STDC_VERSION__ and let me know, &#8216;cuz I sure as hell don&#8217;t!<\/p>\n<p>[3] This example was inspired by <a href=\"https:\/\/bugs.gentoo.org\/show_bug.cgi?id=513386\" target=\"_blank\">bug #513386<\/a>.\u00a0 You can verify that it requires &#8211;std=c++11 by dropping the flag and getting yelled at by the compiler.<\/p>\n<p>[4] Upstream explains why in comment #5 of <a href=\"https:\/\/gcc.gnu.org\/bugzilla\/show_bug.cgi?id=61758\" target=\"_blank\">GCC bug #61758<\/a>.\u00a0 The entire bug is dedicated to this issue.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Gentoo allows users to have multiple versions of gcc installed and we (mostly?) support systems where userland is partially build with different versions.\u00a0 There are both advantages and disadvantages to this and in this post, I&#8217;m going to talk about one of the disadvantages, the C++11 ABI incompatibility problem.\u00a0 I don&#8217;t exactly have a solution, &hellip; <a href=\"https:\/\/blogs.gentoo.org\/blueness\/2015\/03\/10\/the-c11-abi-incompatibility-problem-in-gentoo\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;The C++11 ABI incompatibility problem in Gentoo&#8221;<\/span><\/a><\/p>\n","protected":false},"author":141,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":true},"categories":[1,3],"tags":[],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/posts\/183"}],"collection":[{"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/users\/141"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/comments?post=183"}],"version-history":[{"count":2,"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/posts\/183\/revisions"}],"predecessor-version":[{"id":185,"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/posts\/183\/revisions\/185"}],"wp:attachment":[{"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/media?parent=183"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/categories?post=183"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/blueness\/wp-json\/wp\/v2\/tags?post=183"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}